From 9732e0c744e45a67094fc6ce08bdadb1f9a08d4a Mon Sep 17 00:00:00 2001 From: Hugo Lima Date: Mon, 17 Aug 2009 17:32:08 -0300 Subject: The genesis... --- .gitignore | 4 + AUTHORS | 8 + CMakeLists.txt | 122 + COPYING | 342 +++ Doxyfile | 311 +++ FindApiExtractor.cmake.in | 15 + abstractmetabuilder.cpp | 2556 ++++++++++++++++++ abstractmetabuilder.h | 225 ++ abstractmetalang.cpp | 2175 +++++++++++++++ abstractmetalang.h | 1874 +++++++++++++ apiextractor.cpp | 307 +++ apiextractor.h | 63 + apiextractor.pc.in | 11 + apiextractorversion.h.in | 4 + asttoxml.cpp | 151 ++ asttoxml.h | 40 + cmake_uninstall.cmake | 21 + doc/Makefile | 88 + doc/_static/.gitignore | 0 doc/_static/basic.css | 417 +++ doc/_static/default.css | 248 ++ doc/_static/images/._background_search.jpg | Bin 0 -> 4096 bytes doc/_static/images/._bread_crumb.png | Bin 0 -> 4096 bytes doc/_static/images/._button_search.jpg | Bin 0 -> 4096 bytes doc/_static/images/._side_background.jpg | Bin 0 -> 25192 bytes doc/_static/images/._top_background.jpg | Bin 0 -> 4096 bytes doc/_static/images/background_search.jpg | Bin 0 -> 2129 bytes doc/_static/images/bg.jpg | Bin 0 -> 5749 bytes doc/_static/images/bread_crumb.png | Bin 0 -> 743 bytes doc/_static/images/button_search.png | Bin 0 -> 3259 bytes doc/_static/images/side_background.jpg | Bin 0 -> 13760 bytes doc/_static/images/top_background.jpg | Bin 0 -> 500 bytes doc/_templates/index.html | 26 + doc/_templates/layout.html | 34 + doc/conf.py | 195 ++ doc/contents.rst | 8 + doc/dependency-apiextractor.svg | 360 +++ doc/overview.rst | 15 + doc/typesystem.rst | 810 ++++++ docparser.cpp | 152 ++ docparser.h | 109 + doxygenparser.cpp | 151 ++ doxygenparser.h | 37 + fileout.cpp | 218 ++ fileout.h | 58 + generator.cpp | 146 + generator.h | 358 +++ generator.qrc | 5 + merge.xsl | 82 + parser/ast.cpp | 33 + parser/ast.h | 879 ++++++ parser/binder.cpp | 852 ++++++ parser/binder.h | 125 + parser/class_compiler.cpp | 66 + parser/class_compiler.h | 72 + parser/codemodel.cpp | 932 +++++++ parser/codemodel.h | 838 ++++++ parser/codemodel_finder.cpp | 98 + parser/codemodel_finder.h | 70 + parser/codemodel_fwd.h | 80 + parser/codemodel_pointer.h | 60 + parser/compiler_utils.cpp | 50 + parser/compiler_utils.h | 47 + parser/control.cpp | 130 + parser/control.h | 160 ++ parser/declarator_compiler.cpp | 147 + parser/declarator_compiler.h | 96 + parser/default_visitor.cpp | 459 ++++ parser/default_visitor.h | 118 + parser/dumptree.cpp | 125 + parser/dumptree.h | 46 + parser/lexer.cpp | 1695 ++++++++++++ parser/lexer.h | 290 ++ parser/list.cpp | 28 + parser/list.h | 100 + parser/name_compiler.cpp | 134 + parser/name_compiler.h | 69 + parser/parser.cpp | 4056 ++++++++++++++++++++++++++++ parser/parser.h | 198 ++ parser/r++.macros | 28 + parser/rpp-allocator.h | 24 + parser/rpp/builtin-macros.cpp | 23 + parser/rpp/pp-cctype.h | 57 + parser/rpp/pp-configuration | 86 + parser/rpp/pp-engine-bits.h | 1252 +++++++++ parser/rpp/pp-engine.h | 274 ++ parser/rpp/pp-environment.h | 137 + parser/rpp/pp-fwd.h | 39 + parser/rpp/pp-internal.h | 117 + parser/rpp/pp-iterator.h | 88 + parser/rpp/pp-macro-expander.h | 351 +++ parser/rpp/pp-macro.h | 72 + parser/rpp/pp-main.cpp | 295 ++ parser/rpp/pp-qt-configuration | 24 + parser/rpp/pp-scanner.h | 313 +++ parser/rpp/pp-string.h | 107 + parser/rpp/pp-symbol.h | 87 + parser/rpp/pp.h | 91 + parser/rpp/preprocessor.cpp | 158 ++ parser/rpp/preprocessor.h | 66 + parser/rxx_allocator.h | 130 + parser/smallobject.cpp | 28 + parser/smallobject.h | 47 + parser/symbol.h | 122 + parser/tokens.cpp | 249 ++ parser/tokens.h | 145 + parser/type_compiler.cpp | 129 + parser/type_compiler.h | 71 + parser/visitor.cpp | 122 + parser/visitor.h | 140 + qtdocparser.cpp | 156 ++ qtdocparser.h | 38 + reporthandler.cpp | 100 + reporthandler.h | 154 ++ tests/CMakeLists.txt | 6 + tests/testabstractmetaclass.cpp | 86 + tests/testabstractmetaclass.h | 47 + typeparser.cpp | 261 ++ typeparser.h | 52 + typesystem.cpp | 2085 ++++++++++++++ typesystem.h | 1958 ++++++++++++++ 121 files changed, 33614 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 Doxyfile create mode 100644 FindApiExtractor.cmake.in create mode 100644 abstractmetabuilder.cpp create mode 100644 abstractmetabuilder.h create mode 100644 abstractmetalang.cpp create mode 100644 abstractmetalang.h create mode 100644 apiextractor.cpp create mode 100644 apiextractor.h create mode 100644 apiextractor.pc.in create mode 100644 apiextractorversion.h.in create mode 100644 asttoxml.cpp create mode 100644 asttoxml.h create mode 100644 cmake_uninstall.cmake create mode 100644 doc/Makefile create mode 100644 doc/_static/.gitignore create mode 100644 doc/_static/basic.css create mode 100644 doc/_static/default.css create mode 100755 doc/_static/images/._background_search.jpg create mode 100755 doc/_static/images/._bread_crumb.png create mode 100755 doc/_static/images/._button_search.jpg create mode 100755 doc/_static/images/._side_background.jpg create mode 100755 doc/_static/images/._top_background.jpg create mode 100644 doc/_static/images/background_search.jpg create mode 100644 doc/_static/images/bg.jpg create mode 100644 doc/_static/images/bread_crumb.png create mode 100644 doc/_static/images/button_search.png create mode 100644 doc/_static/images/side_background.jpg create mode 100644 doc/_static/images/top_background.jpg create mode 100644 doc/_templates/index.html create mode 100644 doc/_templates/layout.html create mode 100644 doc/conf.py create mode 100644 doc/contents.rst create mode 100644 doc/dependency-apiextractor.svg create mode 100644 doc/overview.rst create mode 100644 doc/typesystem.rst create mode 100644 docparser.cpp create mode 100644 docparser.h create mode 100644 doxygenparser.cpp create mode 100644 doxygenparser.h create mode 100644 fileout.cpp create mode 100644 fileout.h create mode 100644 generator.cpp create mode 100644 generator.h create mode 100644 generator.qrc create mode 100644 merge.xsl create mode 100644 parser/ast.cpp create mode 100644 parser/ast.h create mode 100644 parser/binder.cpp create mode 100644 parser/binder.h create mode 100644 parser/class_compiler.cpp create mode 100644 parser/class_compiler.h create mode 100644 parser/codemodel.cpp create mode 100644 parser/codemodel.h create mode 100644 parser/codemodel_finder.cpp create mode 100644 parser/codemodel_finder.h create mode 100644 parser/codemodel_fwd.h create mode 100644 parser/codemodel_pointer.h create mode 100644 parser/compiler_utils.cpp create mode 100644 parser/compiler_utils.h create mode 100644 parser/control.cpp create mode 100644 parser/control.h create mode 100644 parser/declarator_compiler.cpp create mode 100644 parser/declarator_compiler.h create mode 100644 parser/default_visitor.cpp create mode 100644 parser/default_visitor.h create mode 100644 parser/dumptree.cpp create mode 100644 parser/dumptree.h create mode 100644 parser/lexer.cpp create mode 100644 parser/lexer.h create mode 100644 parser/list.cpp create mode 100644 parser/list.h create mode 100644 parser/name_compiler.cpp create mode 100644 parser/name_compiler.h create mode 100644 parser/parser.cpp create mode 100644 parser/parser.h create mode 100644 parser/r++.macros create mode 100644 parser/rpp-allocator.h create mode 100644 parser/rpp/builtin-macros.cpp create mode 100644 parser/rpp/pp-cctype.h create mode 100644 parser/rpp/pp-configuration create mode 100644 parser/rpp/pp-engine-bits.h create mode 100644 parser/rpp/pp-engine.h create mode 100644 parser/rpp/pp-environment.h create mode 100644 parser/rpp/pp-fwd.h create mode 100644 parser/rpp/pp-internal.h create mode 100644 parser/rpp/pp-iterator.h create mode 100644 parser/rpp/pp-macro-expander.h create mode 100644 parser/rpp/pp-macro.h create mode 100644 parser/rpp/pp-main.cpp create mode 100644 parser/rpp/pp-qt-configuration create mode 100644 parser/rpp/pp-scanner.h create mode 100644 parser/rpp/pp-string.h create mode 100644 parser/rpp/pp-symbol.h create mode 100644 parser/rpp/pp.h create mode 100644 parser/rpp/preprocessor.cpp create mode 100644 parser/rpp/preprocessor.h create mode 100644 parser/rxx_allocator.h create mode 100644 parser/smallobject.cpp create mode 100644 parser/smallobject.h create mode 100644 parser/symbol.h create mode 100644 parser/tokens.cpp create mode 100644 parser/tokens.h create mode 100644 parser/type_compiler.cpp create mode 100644 parser/type_compiler.h create mode 100644 parser/visitor.cpp create mode 100644 parser/visitor.h create mode 100644 qtdocparser.cpp create mode 100644 qtdocparser.h create mode 100644 reporthandler.cpp create mode 100644 reporthandler.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/testabstractmetaclass.cpp create mode 100644 tests/testabstractmetaclass.h create mode 100644 typeparser.cpp create mode 100644 typeparser.h create mode 100644 typesystem.cpp create mode 100644 typesystem.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..4ef169adc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build +.kdev4 + +apiextractor.kdev4 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..6e802fb53 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,8 @@ +Anderson Lizardo +Bruno Araujo +Hugo Parente Lima +Lauro Moura +Luciano Wolf +Marcelo Lira +Renato Araujo Oliveira Filho + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..a02f37db7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,122 @@ +project(apiextractor) + +cmake_minimum_required(VERSION 2.6) + +find_package(Boost COMPONENTS graph REQUIRED) +find_package(Qt4 4.5.0 REQUIRED) +find_package(PkgConfig) +pkg_check_modules(LIBXML2 REQUIRED libxml-2.0>=2.6.32) +pkg_check_modules(LIBXSLT REQUIRED libxslt>=1.1.19) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -DAPIEXTRACTOR_ENABLE_DUPLICATE_ENUM_VALUES") + +set(apiextractor_VERSION 0.1) +configure_file(apiextractorversion.h.in ${CMAKE_CURRENT_BINARY_DIR}/apiextractorversion.h @ONLY) +set(QT_USE_QTCORE 1) +set(QT_USE_QTXML 1) +include(${QT_USE_FILE}) +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) +add_definitions(-DRXX_ALLOCATOR_INIT_0) + +set(CMAKE_BUILD_TYPE Debug) + +set(apiextractor_SRC +apiextractor.cpp +abstractmetabuilder.cpp +abstractmetalang.cpp +asttoxml.cpp +docparser.cpp +doxygenparser.cpp +qtdocparser.cpp +fileout.cpp +generator.cpp +reporthandler.cpp +typeparser.cpp +typesystem.cpp +parser/ast.cpp +parser/binder.cpp +parser/class_compiler.cpp +parser/codemodel.cpp +parser/codemodel_finder.cpp +parser/compiler_utils.cpp +parser/control.cpp +parser/declarator_compiler.cpp +parser/default_visitor.cpp +parser/dumptree.cpp +parser/lexer.cpp +parser/list.cpp +parser/name_compiler.cpp +parser/parser.cpp +parser/smallobject.cpp +parser/tokens.cpp +parser/type_compiler.cpp +parser/visitor.cpp +parser/rpp/builtin-macros.cpp +parser/rpp/pp-main.cpp +parser/rpp/preprocessor.cpp +) + +qt4_add_resources(apiextractor_RCCS_SRC generator.qrc) + +set(apiextractor_MOC_HEADERS + fileout.h + generator.h +) + +qt4_wrap_cpp(apiextractor_MOC_SRC ${apiextractor_MOC_HEADERS}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/parser + ${CMAKE_CURRENT_SOURCE_DIR}/parser/rpp + ${QT_INCLUDE_DIR} ${Boost_INCLUDE_DIR} + ${LIBXSLT_INCLUDE_DIRS} + ${LIBXML2_INCLUDE_DIRS} + ) + +add_library(apiextractor SHARED ${apiextractor_SRC} ${apiextractor_MOC_SRC} ${apiextractor_RCCS_SRC}) +target_link_libraries(apiextractor ${Boost_GRAPH_LIBRARY} ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTXMLPATTERNS_LIBRARY} ${QT_QTXML_LIBRARY}) +set_target_properties(apiextractor PROPERTIES SOVERSION ${apiextractor_VERSION}) + +# create pkg-config file +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/apiextractor.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/apiextractor.pc @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/FindApiExtractor.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FindApiExtractor.cmake @ONLY) + +# uninstall target +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) +add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + +# "make dist", in fact "make package_source" +set(CPACK_SOURCE_PACKAGE_FILE_NAME "libapiextractor-${apiextractor_VERSION}") +set(CPACK_SOURCE_GENERATOR TGZ) +set(CPACK_SOURCE_IGNORE_FILES "~$" ".svn" "debian/" "build/" ".swp$" "*.kdev4") +include(CPack) + +set(root_HEADERS +abstractmetalang.h +generator.h +apiextractor.h +reporthandler.h +typesystem.h +fileout.h +docparser.h +qtdocparser.h +) + +enable_testing() +add_subdirectory(tests) + +install(FILES ${root_HEADERS} DESTINATION include/apiextractor) +# TODO We do not need to expose these internal headers! +# install(DIRECTORY parser DESTINATION include/apiextractor +# FILES_MATCHING PATTERN "*.h" +# PATTERN ".svn" EXCLUDE +# ) +install(TARGETS apiextractor LIBRARY DESTINATION lib) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/apiextractor.pc DESTINATION lib/pkgconfig) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FindApiExtractor.cmake + DESTINATION share/cmake-2.6/Modules) + diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..4ccd71466 --- /dev/null +++ b/COPYING @@ -0,0 +1,342 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 000000000..ebc397fb9 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,311 @@ +# Doxyfile 1.5.7.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = libgenerator +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = /tmp/src/libgenerator/doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = /tmp/src/libgenerator/ +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /tmp/src/libgenerator +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.F90 \ + *.F \ + *.VHD \ + *.VHDL +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 4 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHG_LOCATION = +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NONE +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = qt4.tag=http://doc.trolltech.com/latest +GENERATE_TAGFILE = libgenerator.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/FindApiExtractor.cmake.in b/FindApiExtractor.cmake.in new file mode 100644 index 000000000..addbca36d --- /dev/null +++ b/FindApiExtractor.cmake.in @@ -0,0 +1,15 @@ +# - try to find APIEXTRACTOR +# APIEXTRACTOR_INCLUDE_DIR - Directories to include to use APIEXTRACTOR +# APIEXTRACTOR_LIBRARIES - Files to link against to use APIEXTRACTOR +# APIEXTRACTOR_FOUND - APIEXTRACTOR was found + +FIND_PATH(APIEXTRACTOR_INCLUDE_DIR apiextractor.h @CMAKE_INSTALL_PREFIX@/include/apiextractor) + +FIND_LIBRARY(APIEXTRACTOR_LIBRARY apiextractor @CMAKE_INSTALL_PREFIX@/lib) + +SET(APIEXTRACTOR_FOUND "NO") +IF(APIEXTRACTOR_LIBRARY AND APIEXTRACTOR_INCLUDE_DIR) + SET(APIEXTRACTOR_FOUND "YES") +ENDIF(APIEXTRACTOR_LIBRARY AND APIEXTRACTOR_INCLUDE_DIR) + + diff --git a/abstractmetabuilder.cpp b/abstractmetabuilder.cpp new file mode 100644 index 000000000..48fc13c23 --- /dev/null +++ b/abstractmetabuilder.cpp @@ -0,0 +1,2556 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "abstractmetabuilder.h" +#include "reporthandler.h" + +#include "parser/ast.h" +#include "parser/binder.h" +#include "parser/control.h" +#include "parser/default_visitor.h" +#include "parser/dumptree.h" +#include "parser/lexer.h" +#include "parser/parser.h" +#include "parser/tokens.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// boost graph library +#include +#include +#include + +#include + +static QString stripTemplateArgs(const QString &name) +{ + int pos = name.indexOf('<'); + return pos < 0 ? name : name.left(pos); +} + +AbstractMetaBuilder::AbstractMetaBuilder() : m_currentClass(0) +{ +} + +AbstractMetaBuilder::~AbstractMetaBuilder() +{ + qDeleteAll(m_globalFunctions); +} + +void AbstractMetaBuilder::checkFunctionModifications() +{ + TypeDatabase *types = TypeDatabase::instance(); + SingleTypeEntryHash entryHash = types->entries(); + QList entries = entryHash.values(); + foreach (TypeEntry *entry, entries) { + if (!entry) + continue; + if (!entry->isComplex() || entry->codeGeneration() == TypeEntry::GenerateNothing) + continue; + + ComplexTypeEntry *centry = static_cast(entry); + FunctionModificationList modifications = centry->functionModifications(); + + foreach (FunctionModification modification, modifications) { + QString signature = modification.signature; + + QString name = signature.trimmed(); + name = name.mid(0, signature.indexOf("(")); + + AbstractMetaClass *clazz = m_metaClasses.findClass(centry->qualifiedCppName()); + if (!clazz) + continue; + + AbstractMetaFunctionList functions = clazz->functions(); + bool found = false; + QStringList possibleSignatures; + foreach (AbstractMetaFunction *function, functions) { + if (function->minimalSignature() == signature && function->implementingClass() == clazz) { + found = true; + break; + } + + if (function->originalName() == name) + possibleSignatures.append(function->minimalSignature() + " in " + function->implementingClass()->name()); + } + + if (!found) { + QString warning + = QString("signature '%1' for function modification in '%2' not found. Possible candidates: %3") + .arg(signature) + .arg(clazz->qualifiedCppName()) + .arg(possibleSignatures.join(", ")); + + ReportHandler::warning(warning); + } + } + } +} + +AbstractMetaClass *AbstractMetaBuilder::argumentToClass(ArgumentModelItem argument) +{ + AbstractMetaClass *returned = 0; + bool ok = false; + AbstractMetaType *type = translateType(argument->type(), &ok); + if (ok && type && type->typeEntry() && type->typeEntry()->isComplex()) { + const TypeEntry *entry = type->typeEntry(); + returned = m_metaClasses.findClass(entry->name()); + } + delete type; + return returned; +} + +/** + * Checks the argument of a hash function and flags the type if it is a complex type + */ +void AbstractMetaBuilder::registerHashFunction(FunctionModelItem function_item) +{ + ArgumentList arguments = function_item->arguments(); + if (arguments.size() == 1) { + if (AbstractMetaClass *cls = argumentToClass(arguments.at(0))) + cls->setHasHashFunction(true); + } +} + +/** + * Check if a class has a debug stream operator that can be used as toString + */ + +void AbstractMetaBuilder::registerToStringCapability(FunctionModelItem function_item) +{ + // TODO This must set an AbstractMetaFunction, not a FunctionModelItem! + #if 0 + ArgumentList arguments = function_item->arguments(); + if (arguments.size() == 2) { + if (arguments.at(0)->type().toString() == "QDebug") { + ArgumentModelItem arg = arguments.at(1); + if (AbstractMetaClass *cls = argumentToClass(arg)) { + if (arg->type().indirections() < 2) + cls->setToStringCapability(function_item); + } + } + } + #endif +} + +void AbstractMetaBuilder::traverseOperatorFunction(FunctionModelItem item) +{ + if (item->accessPolicy() != CodeModel::Public) + return; + + ArgumentList arguments = item->arguments(); + AbstractMetaClass *baseoperandClass; + bool firstArgumentIsSelf = true; + bool unaryOperator = false; + + baseoperandClass = argumentToClass(arguments.at(0)); + if (arguments.size() == 1) { + unaryOperator = true; + } else if (!baseoperandClass) { + baseoperandClass = argumentToClass(arguments.at(1)); + firstArgumentIsSelf = false; + } + + if (baseoperandClass) { + AbstractMetaClass *oldCurrentClass = m_currentClass; + m_currentClass = baseoperandClass; + AbstractMetaFunction *metaFunction = traverseFunction(item); + if (metaFunction && !metaFunction->isInvalid()) { + // Strip away first argument, since that is the containing object + AbstractMetaArgumentList arguments = metaFunction->arguments(); + if (firstArgumentIsSelf || unaryOperator) { + arguments.pop_front(); + metaFunction->setArguments(arguments); + } else { + // If the operator method is not unary and the first operator is + // not of the same type of its owning class we suppose that it + // must be an reverse operator (e.g. CLASS::operator(TYPE, CLASS)). + // All operator overloads that operate over a class are already + // beign added as member functions of that class by the API Extractor, + // in addition to this the reverse operators are marked as static + // for identification purposes. + *metaFunction += AbstractMetaAttributes::Static; + } + metaFunction->setFunctionType(AbstractMetaFunction::NormalFunction); + metaFunction->setVisibility(AbstractMetaFunction::Public); + metaFunction->setOriginalAttributes(metaFunction->attributes()); + setupFunctionDefaults(metaFunction, baseoperandClass); + baseoperandClass->addFunction(metaFunction); + Q_ASSERT(!metaFunction->wasPrivate()); + } else if (metaFunction) { + delete metaFunction; + } + + m_currentClass = oldCurrentClass; + } +} + +void AbstractMetaBuilder::traverseStreamOperator(FunctionModelItem item) +{ + ArgumentList arguments = item->arguments(); + if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) { + AbstractMetaClass *streamClass = argumentToClass(arguments.at(0)); + AbstractMetaClass *streamedClass = argumentToClass(arguments.at(1)); + + if (streamClass && streamedClass + && (streamClass->name() == "QDataStream" || streamClass->name() == "QTextStream")) { + AbstractMetaClass *oldCurrentClass = m_currentClass; + m_currentClass = streamedClass; + AbstractMetaFunction *streamFunction = traverseFunction(item); + + if (streamFunction && !streamFunction->isInvalid()) { + QString name = item->name(); + streamFunction->setFunctionType(AbstractMetaFunction::GlobalScopeFunction); + // Strip first argument, since that is the containing object + //AbstractMetaArgumentList arguments = streamFunction->arguments(); + //arguments.pop_front(); + //streamFunction->setArguments(arguments); + + *streamFunction += AbstractMetaAttributes::Final; + *streamFunction += AbstractMetaAttributes::Public; + streamFunction->setOriginalAttributes(streamFunction->attributes()); + +// streamFunction->setType(0); + + AbstractMetaClass *funcClass; + + if (!streamClass->typeEntry()->generateCode()) { + AbstractMetaArgumentList reverseArgs = reverseList(streamFunction->arguments()); + streamFunction->setArguments(reverseArgs); + streamFunction->setReverseOperator(true); + funcClass = streamedClass; + } else { + funcClass = streamClass; + } + + setupFunctionDefaults(streamFunction, funcClass); + funcClass->addFunction(streamFunction); + if (funcClass == streamClass) + funcClass->typeEntry()->addExtraInclude(streamedClass->typeEntry()->include()); + else + funcClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include()); + + m_currentClass = oldCurrentClass; + } + } + } +} + +void AbstractMetaBuilder::fixQObjectForScope(TypeDatabase *types, + NamespaceModelItem scope) +{ + foreach (ClassModelItem item, scope->classes()) { + QString qualifiedName = item->qualifiedName().join("::"); + TypeEntry *entry = types->findType(qualifiedName); + if (entry) { + if (isQObject(qualifiedName) && entry->isComplex()) + ((ComplexTypeEntry *) entry)->setQObject(true); + } + } + + foreach (NamespaceModelItem item, scope->namespaceMap().values()) { + if (scope != item) + fixQObjectForScope(types, item); + } +} + +void AbstractMetaBuilder::sortLists() +{ + foreach (AbstractMetaClass *cls, m_metaClasses) + cls->sortFunctions(); +} + +bool AbstractMetaBuilder::build(QIODevice* input) +{ + Q_ASSERT(input); + + if (!input->isOpen()) { + if (!input->open(QIODevice::ReadOnly)) + return false; + } + + QByteArray contents = input->readAll(); + input->close(); + + Control control; + Parser p(&control); + pool __pool; + + TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool); + + CodeModel model; + Binder binder(&model, p.location()); + m_dom = binder.run(ast); + + pushScope(model_dynamic_cast(m_dom)); + + QHash typeMap = m_dom->classMap(); + + // fix up QObject's in the type system.. + TypeDatabase *types = TypeDatabase::instance(); + fixQObjectForScope(types, model_dynamic_cast(m_dom)); + + // Start the generation... + QList typeValues = typeMap.values(); + ReportHandler::setProgressReference(typeValues); + foreach (ClassModelItem item, typeValues) { + ReportHandler::progress("Generating class model for %s", qPrintable(item->name())); + AbstractMetaClass *cls = traverseClass(item); + if (!cls) + continue; + + addAbstractMetaClass(cls); + } + + // We need to know all global enums + QHash enumMap = m_dom->enumMap(); + ReportHandler::setProgressReference(enumMap); + foreach (EnumModelItem item, enumMap) { + ReportHandler::progress("Generating enum model for %s", qPrintable(item->name())); + AbstractMetaEnum *metaEnum = traverseEnum(item, 0, QSet()); + if (metaEnum) { + if (metaEnum->typeEntry()->generateCode()) + m_globalEnums << metaEnum; + } + } + + QHash namespaceMap = m_dom->namespaceMap(); + ReportHandler::setProgressReference(namespaceMap); + foreach (NamespaceModelItem item, namespaceMap.values()) { + ReportHandler::progress("Generating namespace model for %s", qPrintable(item->name())); + AbstractMetaClass *metaClass = traverseNamespace(item); + if (metaClass) + m_metaClasses << metaClass; + } + + // Go through all typedefs to see if we have defined any + // specific typedefs to be used as classes. + TypeAliasList typeAliases = m_dom->typeAliases(); + ReportHandler::setProgressReference(typeAliases); + foreach (TypeAliasModelItem typeAlias, typeAliases) { + ReportHandler::progress("Resolving typedefs..."); + AbstractMetaClass *cls = traverseTypeAlias(typeAlias); + addAbstractMetaClass(cls); + } + + ReportHandler::setProgressReference(m_metaClasses); + foreach (AbstractMetaClass *cls, m_metaClasses) { + ReportHandler::progress("Fixing class inheritance..."); + if (!cls->isInterface() && !cls->isNamespace()) + setupInheritance(cls); + } + + ReportHandler::setProgressReference(m_metaClasses); + foreach (AbstractMetaClass *cls, m_metaClasses) { + ReportHandler::progress("Detecting inconsistencies in class model for %s", qPrintable(cls->qualifiedCppName())); + cls->fixFunctions(); + + if (!cls->typeEntry()) { + ReportHandler::warning(QString("class '%1' does not have an entry in the type system") + .arg(cls->name())); + } else { + if (!cls->hasConstructors() && !cls->isFinalInCpp() && !cls->isInterface() && !cls->isNamespace()) + cls->addDefaultConstructor(); + } + + if (cls->isAbstract() && !cls->isInterface()) + cls->typeEntry()->setLookupName(cls->typeEntry()->targetLangName() + "$ConcreteWrapper"); + } + + QList entries = TypeDatabase::instance()->entries().values(); + ReportHandler::setProgressReference(entries); + foreach (const TypeEntry *entry, entries) { + ReportHandler::progress("Detecting inconsistencies in typesystem for %s", qPrintable(entry->name())); + + if (entry->isPrimitive()) + continue; + + if ((entry->isValue() || entry->isObject()) + && !entry->isString() + && !entry->isChar() + && !entry->isContainer() + && !entry->isCustom() + && !entry->isVariant() + && !m_metaClasses.findClass(entry->qualifiedCppName())) { + ReportHandler::warning(QString("type '%1' is specified in typesystem, but not defined. This could potentially lead to compilation errors.") + .arg(entry->qualifiedCppName())); + } + + if (entry->isEnum()) { + QString pkg = entry->targetLangPackage(); + QString name = (pkg.isEmpty() ? QString() : pkg + ".") + + ((EnumTypeEntry *) entry)->targetLangQualifier(); + AbstractMetaClass *cls = m_metaClasses.findClass(name); + + if (cls) { + AbstractMetaEnum *e = cls->findEnum(entry->targetLangName()); + if (!e) + ReportHandler::warning(QString("enum '%1' is specified in typesystem, " + "but not declared") + .arg(entry->qualifiedCppName())); + } + } + } + + { + FunctionList hashFunctions = m_dom->findFunctions("qHash"); + foreach (FunctionModelItem item, hashFunctions) + registerHashFunction(item); + } + + { + FunctionList hashFunctions = m_dom->findFunctions("operator<<"); + foreach (FunctionModelItem item, hashFunctions) + registerToStringCapability(item); + } + + { + FunctionList binaryOperators = m_dom->findFunctions("operator==") + + m_dom->findFunctions("operator!=") + + m_dom->findFunctions("operator<=") + + m_dom->findFunctions("operator>=") + + m_dom->findFunctions("operator<") + + m_dom->findFunctions("operator+") + + m_dom->findFunctions("operator/") + + m_dom->findFunctions("operator*") + + m_dom->findFunctions("operator-") + + m_dom->findFunctions("operator&") + + m_dom->findFunctions("operator|") + + m_dom->findFunctions("operator^") + + m_dom->findFunctions("operator~") + + m_dom->findFunctions("operator>"); + + foreach (FunctionModelItem item, binaryOperators) + traverseOperatorFunction(item); + } + + { + FunctionList streamOperators = m_dom->findFunctions("operator<<") + m_dom->findFunctions("operator>>"); + foreach (FunctionModelItem item, streamOperators) + traverseStreamOperator(item); + } + + figureOutEnumValues(); + figureOutDefaultEnumArguments(); + checkFunctionModifications(); + + // sort all classes topologically + m_metaClasses = classesTopologicalSorted(); + + foreach (AbstractMetaClass *cls, m_metaClasses) { +// setupEquals(cls); +// setupComparable(cls); + setupClonable(cls); + + // sort all inner classes topologically + if (!cls->typeEntry()->codeGeneration() || cls->innerClasses().size() < 2) + continue; + + cls->setInnerClasses(classesTopologicalSorted(cls)); + } + + dumpLog(); + + sortLists(); + + m_currentClass = 0; + + foreach (FunctionModelItem func, m_dom->functions()) { + if (func->accessPolicy() != CodeModel::Public || func->name().startsWith("operator")) + continue; + + AbstractMetaFunction* metaFunc = traverseFunction(func); + if (metaFunc) { + metaFunc->setIncludeFile(func->fileName()); + m_globalFunctions << metaFunc; + } + } + std::puts(""); + return true; +} + +void AbstractMetaBuilder::addAbstractMetaClass(AbstractMetaClass *cls) +{ + if (!cls) + return; + + cls->setOriginalAttributes(cls->attributes()); + if (cls->typeEntry()->isContainer()) { + m_templates << cls; + } else { + m_metaClasses << cls; + if (cls->typeEntry()->designatedInterface()) { + AbstractMetaClass *interface = cls->extractInterface(); + m_metaClasses << interface; + ReportHandler::debugSparse(QString(" -> interface '%1'").arg(interface->name())); + } + } +} + +AbstractMetaClass *AbstractMetaBuilder::traverseNamespace(NamespaceModelItem namespaceItem) +{ + QString namespaceName = (!m_namespacePrefix.isEmpty() ? m_namespacePrefix + "::" : QString()) + namespaceItem->name(); + NamespaceTypeEntry *type = TypeDatabase::instance()->findNamespaceType(namespaceName); + + if (TypeDatabase::instance()->isClassRejected(namespaceName)) { + m_rejectedClasses.insert(namespaceName, GenerationDisabled); + return 0; + } + + if (!type) { + ReportHandler::warning(QString("namespace '%1' does not have a type entry").arg(namespaceName)); + return 0; + } + + AbstractMetaClass *metaClass = createMetaClass(); + metaClass->setTypeEntry(type); + + *metaClass += AbstractMetaAttributes::Public; + + m_currentClass = metaClass; + + ReportHandler::debugSparse(QString("namespace '%1.%2'") + .arg(metaClass->package()) + .arg(namespaceItem->name())); + + traverseEnums(model_dynamic_cast(namespaceItem), metaClass, namespaceItem->enumsDeclarations()); + traverseFunctions(model_dynamic_cast(namespaceItem), metaClass); +// traverseClasses(model_dynamic_cast(namespace_item)); + + pushScope(model_dynamic_cast(namespaceItem)); + m_namespacePrefix = currentScope()->qualifiedName().join("::"); + + ClassList classes = namespaceItem->classes(); + foreach (ClassModelItem cls, classes) { + AbstractMetaClass *mjc = traverseClass(cls); + if (mjc) { + metaClass->addInnerClass(mjc); + mjc->setEnclosingClass(metaClass); + addAbstractMetaClass(mjc); + } + } + + // Go through all typedefs to see if we have defined any + // specific typedefs to be used as classes. + TypeAliasList typeAliases = namespaceItem->typeAliases(); + foreach (TypeAliasModelItem typeAlias, typeAliases) { + AbstractMetaClass *cls = traverseTypeAlias(typeAlias); + if (cls) { + metaClass->addInnerClass(cls); + cls->setEnclosingClass(metaClass); + addAbstractMetaClass(cls); + } + } + + // Traverse namespaces recursively + QList innerNamespaces = namespaceItem->namespaceMap().values(); + foreach (const NamespaceModelItem &ni, innerNamespaces) { + AbstractMetaClass *mjc = traverseNamespace(ni); + if (mjc) { + metaClass->addInnerClass(mjc); + mjc->setEnclosingClass(metaClass); + addAbstractMetaClass(mjc); + } + } + + m_currentClass = 0; + + popScope(); + m_namespacePrefix = currentScope()->qualifiedName().join("::"); + + if (!type->include().isValid()) { + QFileInfo info(namespaceItem->fileName()); + type->setInclude(Include(Include::IncludePath, info.fileName())); + } + + return metaClass; +} + +struct Operator +{ + enum Type { Plus, ShiftLeft, None }; + + Operator() : type(None) {} + + int calculate(int x) + { + switch (type) { + case Plus: return x + value; + case ShiftLeft: return x << value; + case None: return x; + } + return x; + } + + Type type; + int value; +}; + + + +Operator findOperator(QString *s) +{ + const char *names[] = { + "+", + "<<" + }; + + for (int i = 0; i < Operator::None; ++i) { + QString name = QLatin1String(names[i]); + QString str = *s; + int splitPoint = str.indexOf(name); + if (splitPoint > 0) { + bool ok; + QString right = str.mid(splitPoint + name.length()); + Operator op; + op.value = right.toInt(&ok); + if (ok) { + op.type = Operator::Type(i); + *s = str.left(splitPoint).trimmed(); + return op; + } + } + } + return Operator(); +} + +int AbstractMetaBuilder::figureOutEnumValue(const QString &stringValue, + int oldValuevalue, + AbstractMetaEnum *metaEnum, + AbstractMetaFunction *metaFunction) +{ + if (stringValue.isEmpty()) + return oldValuevalue; + + QStringList stringValues = stringValue.split("|"); + + int returnValue = 0; + + bool matched = false; + + for (int i = 0; i < stringValues.size(); ++i) { + QString s = stringValues.at(i).trimmed(); + + bool ok; + int v; + + Operator op = findOperator(&s); + + if (s.length() > 0 && s.at(0) == QLatin1Char('0')) + v = s.toUInt(&ok, 0); + else + v = s.toInt(&ok); + + if (ok) { + matched = true; + + } else if (m_enumValues.contains(s)) { + v = m_enumValues[s]->value(); + matched = true; + + } else { + AbstractMetaEnumValue *ev = 0; + + if (metaEnum && (ev = metaEnum->values().find(s))) { + v = ev->value(); + matched = true; + + } else if (metaEnum && (ev = metaEnum->enclosingClass()->findEnumValue(s, metaEnum))) { + v = ev->value(); + matched = true; + + } else { + if (metaEnum) + ReportHandler::warning("unhandled enum value: " + s + " in " + + metaEnum->enclosingClass()->name() + "::" + + metaEnum->name()); + else + ReportHandler::warning("unhandled enum value: Unknown enum"); + } + } + + if (matched) + returnValue |= op.calculate(v); + } + + if (!matched) { + QString warn = QString("unmatched enum %1").arg(stringValue); + + if (metaFunction) { + warn += QString(" when parsing default value of '%1' in class '%2'") + .arg(metaFunction->name()) + .arg(metaFunction->implementingClass()->name()); + } + + ReportHandler::warning(warn); + returnValue = oldValuevalue; + } + + return returnValue; +} + +void AbstractMetaBuilder::figureOutEnumValuesForClass(AbstractMetaClass *metaClass, + QSet *classes) +{ + AbstractMetaClass *base = metaClass->baseClass(); + + if (base && !classes->contains(base)) + figureOutEnumValuesForClass(base, classes); + + if (classes->contains(metaClass)) + return; + + AbstractMetaEnumList enums = metaClass->enums(); + foreach (AbstractMetaEnum *e, enums) { + if (!e) { + ReportHandler::warning("bad enum in class " + metaClass->name()); + continue; + } + AbstractMetaEnumValueList lst = e->values(); + int value = 0; + for (int i = 0; i < lst.size(); ++i) { + value = figureOutEnumValue(lst.at(i)->stringValue(), value, e); + lst.at(i)->setValue(value); + value++; + } + +#ifndef APIEXTRACTOR_ENABLE_DUPLICATE_ENUM_VALUES + // Check for duplicate values... + EnumTypeEntry *ete = e->typeEntry(); + if (!ete->forceInteger()) { + QHash entries; + foreach (AbstractMetaEnumValue *v, lst) { + + bool vRejected = ete->isEnumValueRejected(v->name()); + + AbstractMetaEnumValue *current = entries.value(v->value()); + if (current) { + bool currentRejected = ete->isEnumValueRejected(current->name()); + if (!currentRejected && !vRejected) { + ReportHandler::warning( + QString("duplicate enum values: %1::%2, %3 and %4 are %5, already rejected: (%6)") + .arg(metaClass->name()) + .arg(e->name()) + .arg(v->name()) + .arg(entries[v->value()]->name()) + .arg(v->value()) + .arg(ete->enumValueRejections().join(", "))); + continue; + } + } + + if (!vRejected) + entries[v->value()] = v; + } + + // Entries now contain all the original entries, no + // rejected ones... Use this to generate the enumValueRedirection table. + foreach (AbstractMetaEnumValue *reject, lst) { + if (!ete->isEnumValueRejected(reject->name())) + continue; + + AbstractMetaEnumValue *used = entries.value(reject->value()); + if (!used) { + ReportHandler::warning( + QString::fromLatin1("Rejected enum has no alternative...: %1::%2\n") + .arg(metaClass->name()) + .arg(reject->name())); + continue; + } + ete->addEnumValueRedirection(reject->name(), used->name()); + } + + } +#endif + } + + *classes += metaClass; +} + + +void AbstractMetaBuilder::figureOutEnumValues() +{ + // Keep a set of classes that we already traversed. We use this to + // enforce that we traverse base classes prior to subclasses. + QSet classes; + foreach (AbstractMetaClass *c, m_metaClasses) + figureOutEnumValuesForClass(c, &classes); +} + +void AbstractMetaBuilder::figureOutDefaultEnumArguments() +{ + foreach (AbstractMetaClass *metaClass, m_metaClasses) { + foreach (AbstractMetaFunction *metaFunction, metaClass->functions()) { + foreach (AbstractMetaArgument *arg, metaFunction->arguments()) { + QString expr = arg->defaultValueExpression(); + if (expr.isEmpty()) + continue; + + if (!metaFunction->replacedDefaultExpression(metaFunction->implementingClass(), + arg->argumentIndex() + 1).isEmpty()) { + continue; + } + + arg->setDefaultValueExpression(expr); + } + } + } +} + + +AbstractMetaEnum *AbstractMetaBuilder::traverseEnum(EnumModelItem enumItem, AbstractMetaClass *enclosing, const QSet &enumsDeclarations) +{ + // Skipping private enums. + if (enumItem->accessPolicy() == CodeModel::Private) + return 0; + + QString qualifiedName = enumItem->qualifiedName().join("::"); + + TypeEntry *typeEntry = TypeDatabase::instance()->findType(qualifiedName); + QString enumName = enumItem->name(); + + QString className; + if (m_currentClass) + className = m_currentClass->typeEntry()->qualifiedCppName(); + + if (TypeDatabase::instance()->isEnumRejected(className, enumName)) { + m_rejectedEnums.insert(qualifiedName, GenerationDisabled); + return 0; + } + + if (!typeEntry || !typeEntry->isEnum()) { + QString context = m_currentClass ? m_currentClass->name() : QLatin1String(""); + ReportHandler::warning(QString("enum '%1' does not have a type entry or is not an enum") + .arg(qualifiedName)); + m_rejectedEnums.insert(qualifiedName, NotInTypeSystem); + return 0; + } + + AbstractMetaEnum *metaEnum = createMetaEnum(); + if (enumsDeclarations.contains(qualifiedName) + || enumsDeclarations.contains(enumName)) { + metaEnum->setHasQEnumsDeclaration(true); + } + + metaEnum->setTypeEntry((EnumTypeEntry *) typeEntry); + switch (enumItem->accessPolicy()) { + case CodeModel::Public: *metaEnum += AbstractMetaAttributes::Public; break; + case CodeModel::Protected: *metaEnum += AbstractMetaAttributes::Protected; break; +// case CodeModel::Private: *meta_enum += AbstractMetaAttributes::Private; break; + default: break; + } + + metaEnum->setIncludeFile(enumItem->fileName()); + + ReportHandler::debugMedium(QString(" - traversing enum %1").arg(metaEnum->fullName())); + + foreach (EnumeratorModelItem value, enumItem->enumerators()) { + + AbstractMetaEnumValue *metaEnumValue = createMetaEnumValue(); + metaEnumValue->setName(value->name()); + // Deciding the enum value... + + metaEnumValue->setStringValue(value->value()); + metaEnum->addEnumValue(metaEnumValue); + + ReportHandler::debugFull(" - " + metaEnumValue->name() + " = " + + metaEnumValue->value()); + + // Add into global register... + if (enclosing) + m_enumValues[enclosing->name() + "::" + metaEnumValue->name()] = metaEnumValue; + else + m_enumValues[metaEnumValue->name()] = metaEnumValue; + } + + m_enums << metaEnum; + + return metaEnum; +} + +AbstractMetaClass* AbstractMetaBuilder::traverseTypeAlias(TypeAliasModelItem typeAlias) +{ + TypeDatabase* types = TypeDatabase::instance(); + QString className = stripTemplateArgs(typeAlias->name()); + + QString fullClassName = className; + // we have an inner class + if (m_currentClass) { + fullClassName = stripTemplateArgs(m_currentClass->typeEntry()->qualifiedCppName()) + + "::" + fullClassName; + } + + // If this is the alias for a primitive type + // we store the aliased type on the alias + // TypeEntry + PrimitiveTypeEntry* ptype = types->findPrimitiveType(className); + if (ptype) { + QString typeAliasName = typeAlias->type().qualifiedName()[0]; + ptype->setAliasedTypeEntry(types->findPrimitiveType(typeAliasName)); + return 0; + } + + // If we haven't specified anything for the typedef, then we don't care + ComplexTypeEntry* type = types->findComplexType(fullClassName); + if (!type) + return 0; + + if (type->isObject()) + static_cast(type)->setQObject(isQObject(stripTemplateArgs(typeAlias->type().qualifiedName().join("::")))); + + AbstractMetaClass *metaClass = createMetaClass(); + metaClass->setTypeAlias(true); + metaClass->setTypeEntry(type); + metaClass->setBaseClassNames(QStringList() << typeAlias->type().qualifiedName().join("::")); + *metaClass += AbstractMetaAttributes::Public; + + // Set the default include file name + if (!type->include().isValid()) { + QFileInfo info(typeAlias->fileName()); + type->setInclude(Include(Include::IncludePath, info.fileName())); + } + + return metaClass; +} + +AbstractMetaClass *AbstractMetaBuilder::traverseClass(ClassModelItem classItem) +{ + QString className = stripTemplateArgs(classItem->name()); + QString fullClassName = className; + + // we have inner an class + if (m_currentClass) { + fullClassName = stripTemplateArgs(m_currentClass->typeEntry()->qualifiedCppName()) + + "::" + fullClassName; + } + + ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(fullClassName); + RejectReason reason = NoReason; + + if (fullClassName == "QMetaTypeId") { + // QtScript: record which types have been declared + int lpos = classItem->name().indexOf('<'); + int rpos = classItem->name().lastIndexOf('>'); + if ((lpos != -1) && (rpos != -1)) { + QString declaredTypename = classItem->name().mid(lpos + 1, rpos - lpos - 1); + m_qmetatypeDeclaredTypenames.insert(declaredTypename); + } + } + + if (TypeDatabase::instance()->isClassRejected(fullClassName)) { + reason = GenerationDisabled; + } else if (!type) { + TypeEntry *te = TypeDatabase::instance()->findType(fullClassName); + if (te && !te->isComplex()) + reason = RedefinedToNotClass; + else + reason = NotInTypeSystem; + } else if (type->codeGeneration() == TypeEntry::GenerateNothing) { + reason = GenerationDisabled; + } + + if (reason != NoReason) { + m_rejectedClasses.insert(fullClassName, reason); + return 0; + } + + if (type->isObject()) + ((ObjectTypeEntry *)type)->setQObject(isQObject(fullClassName)); + + AbstractMetaClass *metaClass = createMetaClass(); + metaClass->setTypeEntry(type); + metaClass->setBaseClassNames(classItem->baseClasses()); + *metaClass += AbstractMetaAttributes::Public; + + AbstractMetaClass *oldCurrentClass = m_currentClass; + m_currentClass = metaClass; + + if (type->isContainer()) + ReportHandler::debugSparse(QString("container: '%1'").arg(fullClassName)); + else + ReportHandler::debugSparse(QString("class: '%1'").arg(metaClass->fullName())); + + TemplateParameterList template_parameters = classItem->templateParameters(); + QList template_args; + template_args.clear(); + for (int i = 0; i < template_parameters.size(); ++i) { + const TemplateParameterModelItem ¶m = template_parameters.at(i); + TemplateArgumentEntry *param_type = new TemplateArgumentEntry(param->name()); + param_type->setOrdinal(i); + template_args.append(param_type); + } + metaClass->setTemplateArguments(template_args); + + parseQ_Property(metaClass, classItem->propertyDeclarations()); + + traverseEnums(model_dynamic_cast(classItem), metaClass, classItem->enumsDeclarations()); + traverseFields(model_dynamic_cast(classItem), metaClass); + traverseFunctions(model_dynamic_cast(classItem), metaClass); + + // Inner classes + { + QList innerClasses = classItem->classMap().values(); + foreach (const ClassModelItem &ci, innerClasses) { + AbstractMetaClass *cl = traverseClass(ci); + if (cl) { + cl->setEnclosingClass(metaClass); + metaClass->addInnerClass(cl); + m_metaClasses << cl; + } + } + + } + + // Go through all typedefs to see if we have defined any + // specific typedefs to be used as classes. + TypeAliasList typeAliases = classItem->typeAliases(); + foreach (TypeAliasModelItem typeAlias, typeAliases) { + AbstractMetaClass *cls = traverseTypeAlias(typeAlias); + if (cls) { + cls->setEnclosingClass(metaClass); + addAbstractMetaClass(cls); + } + } + + + m_currentClass = oldCurrentClass; + + // Set the default include file name + if (!type->include().isValid()) { + QFileInfo info(classItem->fileName()); + type->setInclude(Include(Include::IncludePath, info.fileName())); + } + + return metaClass; +} + +AbstractMetaField *AbstractMetaBuilder::traverseField(VariableModelItem field, const AbstractMetaClass *cls) +{ + QString fieldName = field->name(); + QString className = m_currentClass->typeEntry()->qualifiedCppName(); + + // Ignore friend decl. + if (field->isFriend()) + return 0; + + if (field->accessPolicy() == CodeModel::Private) + return 0; + + if (TypeDatabase::instance()->isFieldRejected(className, fieldName)) { + m_rejectedFields.insert(className + "::" + fieldName, GenerationDisabled); + return 0; + } + + + AbstractMetaField *metaField = createMetaField(); + metaField->setName(fieldName); + metaField->setEnclosingClass(cls); + + bool ok; + TypeInfo fieldType = field->type(); + AbstractMetaType *metaType = translateType(fieldType, &ok); + + if (!metaType || !ok) { + ReportHandler::warning(QString("skipping field '%1::%2' with unmatched type '%3'") + .arg(m_currentClass->name()) + .arg(fieldName) + .arg(TypeInfo::resolveType(fieldType, currentScope()->toItem()).qualifiedName().join("::"))); + delete metaField; + return 0; + } + + metaField->setType(metaType); + + uint attr = 0; + if (field->isStatic()) + attr |= AbstractMetaAttributes::Static; + + CodeModel::AccessPolicy policy = field->accessPolicy(); + if (policy == CodeModel::Public) + attr |= AbstractMetaAttributes::Public; + else if (policy == CodeModel::Protected) + attr |= AbstractMetaAttributes::Protected; + else + attr |= AbstractMetaAttributes::Private; + metaField->setAttributes(attr); + + return metaField; +} + +void AbstractMetaBuilder::traverseFields(ScopeModelItem scope_item, AbstractMetaClass *metaClass) +{ + foreach (VariableModelItem field, scope_item->variables()) { + AbstractMetaField *metaField = traverseField(field, metaClass); + + if (metaField) { + metaField->setOriginalAttributes(metaField->attributes()); + metaClass->addField(metaField); + } + } +} + +void AbstractMetaBuilder::setupFunctionDefaults(AbstractMetaFunction *metaFunction, AbstractMetaClass *metaClass) +{ + // Set the default value of the declaring class. This may be changed + // in fixFunctions later on + metaFunction->setDeclaringClass(metaClass); + + // Some of the queries below depend on the implementing class being set + // to function properly. Such as function modifications + metaFunction->setImplementingClass(metaClass); + + if (metaFunction->name() == "operator_equal") + metaClass->setHasEqualsOperator(true); + + if (!metaFunction->isFinalInTargetLang() + && metaFunction->isRemovedFrom(metaClass, TypeSystem::TargetLangCode)) { + *metaFunction += AbstractMetaAttributes::FinalInCpp; + } +} + +void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scopeItem, AbstractMetaClass *metaClass) +{ + foreach (FunctionModelItem function, scopeItem->functions()) { + AbstractMetaFunction *metaFunction = traverseFunction(function); + + if (metaFunction) { + metaFunction->setOriginalAttributes(metaFunction->attributes()); + if (metaClass->isNamespace()) + *metaFunction += AbstractMetaAttributes::Static; + + if (QPropertySpec *read = metaClass->propertySpecForRead(metaFunction->name())) { + if (read->type() == metaFunction->type()->typeEntry()) { + *metaFunction += AbstractMetaAttributes::PropertyReader; + metaFunction->setPropertySpec(read); + } + } else if (QPropertySpec *write = metaClass->propertySpecForWrite(metaFunction->name())) { + if (write->type() == metaFunction->arguments().at(0)->type()->typeEntry()) { + *metaFunction += AbstractMetaAttributes::PropertyWriter; + metaFunction->setPropertySpec(write); + } + } else if (QPropertySpec *reset = metaClass->propertySpecForReset(metaFunction->name())) { + *metaFunction += AbstractMetaAttributes::PropertyResetter; + metaFunction->setPropertySpec(reset); + } + + + bool isInvalidDestructor = metaFunction->isDestructor() && metaFunction->isPrivate(); + bool isInvalidConstructor = metaFunction->isConstructor() + && (metaFunction->isPrivate() || metaFunction->isInvalid()); + if ((isInvalidDestructor || isInvalidConstructor) + && !metaClass->hasNonPrivateConstructor()) { + *metaClass += AbstractMetaAttributes::Final; + } else if (metaFunction->isConstructor() && !metaFunction->isPrivate()) { + *metaClass -= AbstractMetaAttributes::Final; + metaClass->setHasNonPrivateConstructor(true); + } + + // Classes with virtual destructors should always have a shell class + // (since we aren't registering the destructors, we need this extra check) + if (metaFunction->isDestructor() && !metaFunction->isFinal()) + metaClass->setForceShellClass(true); + + if (!metaFunction->isDestructor() + && !metaFunction->isInvalid() + && (!metaFunction->isConstructor() || !metaFunction->isPrivate())) { + + setupFunctionDefaults(metaFunction, metaClass); + + if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction)) { + QString warn = QString("signal '%1' in class '%2' is overloaded.") + .arg(metaFunction->name()).arg(metaClass->name()); + ReportHandler::warning(warn); + } + + if (metaFunction->isSignal() && !metaClass->isQObject()) { + QString warn = QString("signal '%1' in non-QObject class '%2'") + .arg(metaFunction->name()).arg(metaClass->name()); + ReportHandler::warning(warn); + } + + metaClass->addFunction(metaFunction); + } else if (metaFunction->isDestructor() && metaFunction->isPrivate()) { + metaClass->setHasPrivateDestructor(true); + } + applyFunctionModifications(metaFunction); + } + } +} + +void AbstractMetaBuilder::applyFunctionModifications(AbstractMetaFunction* func) +{ + FunctionModificationList mods = func->modifications(func->implementingClass()); + AbstractMetaFunction& funcRef = *func; + foreach (FunctionModification mod, mods) { + if (mod.isRenameModifier()) { + func->setOriginalName(func->name()); + func->setName(mod.renamedTo()); + } else if (mod.isAccessModifier()) { + funcRef -= AbstractMetaAttributes::Public; + funcRef -= AbstractMetaAttributes::Protected; + funcRef -= AbstractMetaAttributes::Private; + funcRef -= AbstractMetaAttributes::Friendly; + + if (mod.isPublic()) + funcRef += AbstractMetaAttributes::Public; + else if (mod.isProtected()) + funcRef += AbstractMetaAttributes::Protected; + else if (mod.isPrivate()) + funcRef += AbstractMetaAttributes::Private; + else if (mod.isFriendly()) + funcRef += AbstractMetaAttributes::Friendly; + } + + if (mod.isFinal()) + funcRef += AbstractMetaAttributes::FinalInTargetLang; + else if (mod.isNonFinal()) + funcRef -= AbstractMetaAttributes::FinalInTargetLang; + } +} + +bool AbstractMetaBuilder::setupInheritance(AbstractMetaClass *metaClass) +{ + Q_ASSERT(!metaClass->isInterface()); + + if (m_setupInheritanceDone.contains(metaClass)) + return true; + + m_setupInheritanceDone.insert(metaClass); + + QStringList baseClasses = metaClass->baseClassNames(); + + TypeDatabase *types = TypeDatabase::instance(); + + // we only support our own containers and ONLY if there is only one baseclass + if (baseClasses.size() == 1 && baseClasses.first().count('<') == 1) { + QStringList scope = metaClass->typeEntry()->qualifiedCppName().split("::"); + scope.removeLast(); + for (int i = scope.size(); i >= 0; --i) { + QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join("::") + "::" : QString(); + QString completeName = prefix + baseClasses.first(); + TypeParser::Info info = TypeParser::parse(completeName); + QString baseName = info.qualified_name.join("::"); + + AbstractMetaClass *templ = 0; + foreach (AbstractMetaClass *c, m_templates) { + if (c->typeEntry()->name() == baseName) { + templ = c; + break; + } + } + + if (!templ) + templ = m_metaClasses.findClass(baseName); + + if (templ) { + setupInheritance(templ); + inheritTemplate(metaClass, templ, info); + return true; + } + } + + ReportHandler::warning(QString("template baseclass '%1' of '%2' is not known") + .arg(baseClasses.first()) + .arg(metaClass->name())); + return false; + } + + int primary = -1; + int primaries = 0; + for (int i = 0; i < baseClasses.size(); ++i) { + + if (types->isClassRejected(baseClasses.at(i))) + continue; + + TypeEntry *baseClassEntry = types->findType(baseClasses.at(i)); + if (!baseClassEntry) + ReportHandler::warning(QString("class '%1' inherits from unknown base class '%2'") + .arg(metaClass->name()).arg(baseClasses.at(i))); + + // true for primary base class + else if (!baseClassEntry->designatedInterface()) { + if (primaries > 0) { + ReportHandler::warning(QString("class '%1' has multiple primary base classes" + " '%2' and '%3'") + .arg(metaClass->name()) + .arg(baseClasses.at(primary)) + .arg(baseClassEntry->name())); + return false; + } + primaries++; + primary = i; + } + } + + if (primary >= 0) { + AbstractMetaClass *baseClass = m_metaClasses.findClass(baseClasses.at(primary)); + if (!baseClass) { + ReportHandler::warning(QString("unknown baseclass for '%1': '%2'") + .arg(metaClass->name()) + .arg(baseClasses.at(primary))); + return false; + } + metaClass->setBaseClass(baseClass); + } + + for (int i = 0; i < baseClasses.size(); ++i) { + if (types->isClassRejected(baseClasses.at(i))) + continue; + + if (i != primary) { + AbstractMetaClass *baseClass = m_metaClasses.findClass(baseClasses.at(i)); + if (!baseClass) { + ReportHandler::warning(QString("class not found for setup inheritance '%1'").arg(baseClasses.at(i))); + return false; + } + + setupInheritance(baseClass); + + QString interfaceName = InterfaceTypeEntry::interfaceName(baseClass->name()); + AbstractMetaClass *iface = m_metaClasses.findClass(interfaceName); + if (!iface) { + ReportHandler::warning(QString("unknown interface for '%1': '%2'") + .arg(metaClass->name()) + .arg(interfaceName)); + return false; + } + metaClass->addInterface(iface); + + AbstractMetaClassList interfaces = iface->interfaces(); + foreach (AbstractMetaClass *iface, interfaces) + metaClass->addInterface(iface); + } + } + + return true; +} + +void AbstractMetaBuilder::traverseEnums(ScopeModelItem scopeItem, AbstractMetaClass *metaClass, const QStringList &enumsDeclarations) +{ + EnumList enums = scopeItem->enums(); + foreach (EnumModelItem enum_item, enums) { + AbstractMetaEnum *meta_enum = traverseEnum(enum_item, metaClass, QSet::fromList(enumsDeclarations)); + if (meta_enum) { + meta_enum->setOriginalAttributes(meta_enum->attributes()); + metaClass->addEnum(meta_enum); + meta_enum->setEnclosingClass(metaClass); + } + } +} + +AbstractMetaFunction *AbstractMetaBuilder::traverseFunction(FunctionModelItem functionItem) +{ + QString functionName = functionItem->name(); + QString className; + if (m_currentClass) + className = m_currentClass->typeEntry()->qualifiedCppName(); + + if (TypeDatabase::instance()->isFunctionRejected(className, functionName)) { + m_rejectedFunctions.insert(className + "::" + functionName, GenerationDisabled); + return 0; + } + + Q_ASSERT(functionItem->functionType() == CodeModel::Normal + || functionItem->functionType() == CodeModel::Signal + || functionItem->functionType() == CodeModel::Slot); + + if (functionItem->isFriend()) + return 0; + + + QString cast_type; + + AbstractMetaFunction *metaFunction = createMetaFunction(); + metaFunction->setConstant(functionItem->isConstant()); + + ReportHandler::debugMedium(QString(" - %2()").arg(functionName)); + + metaFunction->setName(functionName); + metaFunction->setOriginalName(functionItem->name()); + + if (functionItem->isAbstract()) + *metaFunction += AbstractMetaAttributes::Abstract; + + if (!metaFunction->isAbstract()) + *metaFunction += AbstractMetaAttributes::Native; + + if (!functionItem->isVirtual()) + *metaFunction += AbstractMetaAttributes::Final; + + if (functionItem->isInvokable()) + *metaFunction += AbstractMetaAttributes::Invokable; + + if (functionItem->isStatic()) { + *metaFunction += AbstractMetaAttributes::Static; + *metaFunction += AbstractMetaAttributes::Final; + } + + // Access rights + if (functionItem->accessPolicy() == CodeModel::Public) + *metaFunction += AbstractMetaAttributes::Public; + else if (functionItem->accessPolicy() == CodeModel::Private) + *metaFunction += AbstractMetaAttributes::Private; + else + *metaFunction += AbstractMetaAttributes::Protected; + + + QString strippedClassName = className; + int cc_pos = strippedClassName.lastIndexOf("::"); + if (cc_pos > 0) + strippedClassName = strippedClassName.mid(cc_pos + 2); + + TypeInfo functionType = functionItem->type(); + if (functionName.startsWith('~')) { + metaFunction->setFunctionType(AbstractMetaFunction::DestructorFunction); + metaFunction->setInvalid(true); + } else if (stripTemplateArgs(functionName) == strippedClassName) { + metaFunction->setFunctionType(AbstractMetaFunction::ConstructorFunction); + metaFunction->setExplicit(functionItem->isExplicit()); + metaFunction->setName(m_currentClass->name()); + } else { + bool ok; + AbstractMetaType *type = 0; + + if (!cast_type.isEmpty()) { + TypeInfo info; + info.setQualifiedName(QStringList(cast_type)); + type = translateType(info, &ok); + } else { + type = translateType(functionType, &ok); + } + + if (!ok) { + ReportHandler::warning(QString("skipping function '%1::%2', unmatched return type '%3'") + .arg(className) + .arg(functionItem->name()) + .arg(functionItem->type().toString())); + m_rejectedFunctions[className + "::" + functionName] = + UnmatchedReturnType; + metaFunction->setInvalid(true); + return metaFunction; + } + metaFunction->setType(type); + + if (functionItem->functionType() == CodeModel::Signal) + metaFunction->setFunctionType(AbstractMetaFunction::SignalFunction); + else if (functionItem->functionType() == CodeModel::Slot) + metaFunction->setFunctionType(AbstractMetaFunction::SlotFunction); + } + + ArgumentList arguments = functionItem->arguments(); + AbstractMetaArgumentList metaArguments; + + int firstDefaultArgument = 0; + for (int i = 0; i < arguments.size(); ++i) { + ArgumentModelItem arg = arguments.at(i); + + bool ok; + AbstractMetaType *metaType = translateType(arg->type(), &ok); + if (!metaType || !ok) { + ReportHandler::warning(QString("skipping function '%1::%2', " + "unmatched parameter type '%3'") + .arg(className) + .arg(functionItem->name()) + .arg(arg->type().toString())); + m_rejectedFunctions[className + "::" + functionName] = + UnmatchedArgumentType; + metaFunction->setInvalid(true); + return metaFunction; + } + AbstractMetaArgument *metaArgument = createMetaArgument(); + metaArgument->setType(metaType); + metaArgument->setName(arg->name()); + metaArgument->setArgumentIndex(i); + metaArguments << metaArgument; + } + + metaFunction->setArguments(metaArguments); + + // Find the correct default values + for (int i = 0; i < arguments.size(); ++i) { + ArgumentModelItem arg = arguments.at(i); + AbstractMetaArgument *metaArg = metaArguments.at(i); + + //use relace-default-expression for set default value + QString replacedExpression; + if (m_currentClass) + replacedExpression = metaFunction->replacedDefaultExpression(m_currentClass, i + 1); + + if (arg->defaultValue() || !replacedExpression.isEmpty()) { + QString expr = arg->defaultValueExpression(); + + if (!expr.isEmpty()) + metaArg->setOriginalDefaultValueExpression(expr); + + if (m_currentClass) { + expr = translateDefaultValue(arg, metaArg->type(), metaFunction, m_currentClass, i); + metaArg->setDefaultValueExpression(expr); + } + + if (expr.isEmpty()) + firstDefaultArgument = i; + + if (metaArg->type()->isEnum() || metaArg->type()->isFlags()) + m_enumDefaultArguments << QPair(metaArg, metaFunction); + } + } + +#if 0 + // If we where not able to translate the default argument make it + // reset all default arguments before this one too. + for (int i = 0; i < first_default_argument; ++i) + meta_arguments[i]->setDefaultValueExpression("" + QString()); + + if (ReportHandler::debugLevel() == ReportHandler::FullDebug) + foreach (AbstractMetaArgument *arg, meta_arguments) + ReportHandler::debugFull(" - " + arg->toString()); +#endif + + return metaFunction; +} + + +AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, bool *ok, bool resolveType, bool resolveScope) +{ + Q_ASSERT(ok); + *ok = true; + + // 1. Test the type info without resolving typedefs in case this is present in the + // type system + TypeInfo typei; + if (resolveType) { + bool ok; + AbstractMetaType *t = translateType(_typei, &ok, false, resolveScope); + if (t && ok) + return t; + } + + if (!resolveType) { + typei = _typei; + } else { + // Go through all parts of the current scope (including global namespace) + // to resolve typedefs. The parser does not properly resolve typedefs in + // the global scope when they are referenced from inside a namespace. + // This is a work around to fix this bug since fixing it in resolveType + // seemed non-trivial + int i = m_scopes.size() - 1; + while (i >= 0) { + typei = TypeInfo::resolveType(_typei, m_scopes.at(i--)->toItem()); + if (typei.qualifiedName().join("::") != _typei.qualifiedName().join("::")) + break; + } + + } + + if (typei.isFunctionPointer()) { + *ok = false; + return 0; + } + + TypeParser::Info typeInfo = TypeParser::parse(typei.toString()); + if (typeInfo.is_busted) { + *ok = false; + return 0; + } + + // 2. Handle pointers specified as arrays with unspecified size + bool arrayOfUnspecifiedSize = false; + if (typeInfo.arrays.size() > 0) { + arrayOfUnspecifiedSize = true; + for (int i = 0; i < typeInfo.arrays.size(); ++i) + arrayOfUnspecifiedSize = arrayOfUnspecifiedSize && typeInfo.arrays.at(i).isEmpty(); + + if (!arrayOfUnspecifiedSize) { + TypeInfo newInfo; + //newInfo.setArguments(typei.arguments()); + newInfo.setIndirections(typei.indirections()); + newInfo.setConstant(typei.isConstant()); + newInfo.setFunctionPointer(typei.isFunctionPointer()); + newInfo.setQualifiedName(typei.qualifiedName()); + newInfo.setReference(typei.isReference()); + newInfo.setVolatile(typei.isVolatile()); + + AbstractMetaType *elementType = translateType(newInfo, ok); + if (!(*ok)) + return 0; + + for (int i = typeInfo.arrays.size() - 1; i >= 0; --i) { + QString s = typeInfo.arrays.at(i); + bool ok; + + int elems = s.toInt(&ok); + if (!ok) + return 0; + + AbstractMetaType *arrayType = createMetaType(); + arrayType->setArrayElementCount(elems); + arrayType->setArrayElementType(elementType); + arrayType->setTypeEntry(new ArrayTypeEntry(elementType->typeEntry())); + decideUsagePattern(arrayType); + + elementType = arrayType; + } + + return elementType; + } else { + typeInfo.indirections += typeInfo.arrays.size(); + } + } + + QStringList qualifierList = typeInfo.qualified_name; + if (qualifierList.isEmpty()) { + ReportHandler::warning(QString("horribly broken type '%1'").arg(_typei.toString())); + *ok = false; + return 0; + } + + QString qualifiedName = qualifierList.join("::"); + QString name = qualifierList.takeLast(); + + // 3. Special case 'void' type + if (name == "void" && !typeInfo.indirections) + return 0; + + // 4. Special case QFlags (include instantiation in name) + if (qualifiedName == "QFlags") + qualifiedName = typeInfo.toString(); + + // 5. Try to find the type + const TypeEntry *type = TypeDatabase::instance()->findType(qualifiedName); + + // 6. No? Try looking it up as a flags type + if (!type) + type = TypeDatabase::instance()->findFlagsType(qualifiedName); + + // 7. No? Try looking it up as a container type + if (!type) + type = TypeDatabase::instance()->findContainerType(name); + + // 8. No? Check if the current class is a template and this type is one + // of the parameters. + if (!type && m_currentClass) { + QList template_args = m_currentClass->templateArguments(); + foreach (TypeEntry *te, template_args) { + if (te->name() == qualifiedName) + type = te; + } + } + + // 9. Try finding the type by prefixing it with the current + // context and all baseclasses of the current context + if (!type && !TypeDatabase::instance()->isClassRejected(qualifiedName) && m_currentClass && resolveScope) { + QStringList contexts; + contexts.append(m_currentClass->qualifiedCppName()); + contexts.append(currentScope()->qualifiedName().join("::")); + + + TypeInfo info = typei; + bool subclassesDone = false; + while (!contexts.isEmpty() && !type) { + //type = TypeDatabase::instance()->findType(contexts.at(0) + "::" + qualified_name); + + bool ok; + info.setQualifiedName(QStringList() << contexts.at(0) << qualifiedName); + AbstractMetaType *t = translateType(info, &ok, true, false); + if (t && ok) + return t; + + ClassModelItem item = m_dom->findClass(contexts.at(0)); + if (item) + contexts += item->baseClasses(); + contexts.pop_front(); + + // 10. Last resort: Special cased prefix of Qt namespace since the meta object implicitly inherits this, so + // enum types from there may be addressed without any scope resolution in properties. + if (!contexts.size() && !subclassesDone) { + contexts << "Qt"; + subclassesDone = true; + } + } + + } + + if (!type) { + *ok = false; + return 0; + } + + // Used to for diagnostics later... + m_usedTypes << type; + + // These are only implicit and should not appear in code... + Q_ASSERT(!type->isInterface()); + + AbstractMetaType *metaType = createMetaType(); + metaType->setTypeEntry(type); + metaType->setIndirections(typeInfo.indirections); + metaType->setReference(typeInfo.is_reference); + metaType->setConstant(typeInfo.is_constant); + metaType->setOriginalTypeDescription(_typei.toString()); + decideUsagePattern(metaType); + + if (metaType->typeEntry()->isContainer()) { + ContainerTypeEntry::Type container_type = static_cast(type)->type(); + + if (container_type == ContainerTypeEntry::StringListContainer) { + TypeInfo info; + info.setQualifiedName(QStringList() << "QString"); + AbstractMetaType *targType = translateType(info, ok); + + Q_ASSERT(*ok); + Q_ASSERT(targType); + + metaType->addInstantiation(targType); + metaType->setInstantiationInCpp(false); + + } else { + foreach (const TypeParser::Info &ta, typeInfo.template_instantiations) { + TypeInfo info; + info.setConstant(ta.is_constant); + info.setReference(ta.is_reference); + info.setIndirections(ta.indirections); + + info.setFunctionPointer(false); + info.setQualifiedName(ta.instantiationName().split("::")); + + AbstractMetaType *targType = translateType(info, ok); + if (!(*ok)) { + delete metaType; + return 0; + } + + metaType->addInstantiation(targType); + } + } + + if (container_type == ContainerTypeEntry::ListContainer + || container_type == ContainerTypeEntry::VectorContainer + || container_type == ContainerTypeEntry::StringListContainer) { + Q_ASSERT(metaType->instantiations().size() == 1); + } + } + + return metaType; +} + +void AbstractMetaBuilder::decideUsagePattern(AbstractMetaType *metaType) +{ + const TypeEntry *type = metaType->typeEntry(); + + if (type->isPrimitive() && (!metaType->actualIndirections() + || (metaType->isConstant() && metaType->isReference() && !metaType->indirections()))) { + metaType->setTypeUsagePattern(AbstractMetaType::PrimitivePattern); + + } else if (type->isVoid()) { + metaType->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); + + } else if (type->isString() + && metaType->indirections() == 0 + && (metaType->isConstant() == metaType->isReference() + || metaType->isConstant())) { + metaType->setTypeUsagePattern(AbstractMetaType::StringPattern); + + } else if (type->isChar() + && !metaType->indirections() + && metaType->isConstant() == metaType->isReference()) { + metaType->setTypeUsagePattern(AbstractMetaType::CharPattern); + + } else if (type->isJObjectWrapper() + && !metaType->indirections() + && metaType->isConstant() == metaType->isReference()) { + metaType->setTypeUsagePattern(AbstractMetaType::JObjectWrapperPattern); + + } else if (type->isVariant() + && !metaType->indirections() + && metaType->isConstant() == metaType->isReference()) { + metaType->setTypeUsagePattern(AbstractMetaType::VariantPattern); + + } else if (type->isEnum() && !metaType->actualIndirections()) { + metaType->setTypeUsagePattern(AbstractMetaType::EnumPattern); + + } else if (type->isObject() + && metaType->indirections() == 0 + && metaType->isReference()) { + if (((ComplexTypeEntry *) type)->isQObject()) + metaType->setTypeUsagePattern(AbstractMetaType::QObjectPattern); + else + metaType->setTypeUsagePattern(AbstractMetaType::ObjectPattern); + + } else if (type->isObject() + && metaType->indirections() == 1) { + if (((ComplexTypeEntry *) type)->isQObject()) + metaType->setTypeUsagePattern(AbstractMetaType::QObjectPattern); + else + metaType->setTypeUsagePattern(AbstractMetaType::ObjectPattern); + + // const-references to pointers can be passed as pointers + if (metaType->isReference() && metaType->isConstant()) { + metaType->setReference(false); + metaType->setConstant(false); + } + + } else if (type->isContainer() && !metaType->indirections()) { + metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); + + } else if (type->isTemplateArgument()) { + + } else if (type->isFlags() + && !metaType->indirections() + && (metaType->isConstant() == metaType->isReference())) { + metaType->setTypeUsagePattern(AbstractMetaType::FlagsPattern); + + } else if (type->isArray()) { + metaType->setTypeUsagePattern(AbstractMetaType::ArrayPattern); + + } else if (type->isThread()) { + Q_ASSERT(metaType->indirections() == 1); + metaType->setTypeUsagePattern(AbstractMetaType::ThreadPattern); + + } else if (type->isValue() + && !metaType->indirections() + && (metaType->isConstant() == metaType->isReference() + || !metaType->isReference())) { + metaType->setTypeUsagePattern(AbstractMetaType::ValuePattern); + + } else { + metaType->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); + ReportHandler::debugFull(QString("native pointer pattern for '%1'") + .arg(metaType->cppSignature())); + } +} + +QString AbstractMetaBuilder::translateDefaultValue(ArgumentModelItem item, AbstractMetaType *type, + AbstractMetaFunction *fnc, AbstractMetaClass *implementingClass, + int argumentIndex) +{ + QString functionName = fnc->name(); + QString className = implementingClass->qualifiedCppName(); + + QString replacedExpression = fnc->replacedDefaultExpression(implementingClass, argumentIndex + 1); + if (fnc->removedDefaultExpression(implementingClass, argumentIndex + 1)) + return ""; + else if (!replacedExpression.isEmpty()) + return replacedExpression; + + QString expr = item->defaultValueExpression(); + if (type) { + if (type->isPrimitive()) { + if (type->name() == "boolean") { + if (expr != "false" && expr != "true") { + bool ok = false; + int number = expr.toInt(&ok); + if (ok && number) + expr = "true"; + else + expr = "false"; + } + } else if (expr == "QVariant::Invalid") { + expr = QString::number(QVariant::Invalid); + } else { + // This can be an enum or flag so I need to delay the + // translation untill all namespaces are completly + // processed. This is done in figureOutEnumValues() + } + } else if (type->isFlags() || type->isEnum()) { + bool isNumber; + expr.toInt(&isNumber); + if (!isNumber && expr.indexOf("::") < 0) { + // Add the enum/flag scope to default value, making it usable + // from other contexts beside its owner class hierarchy + QRegExp typeRegEx("[^<]*[<]([^:]*::).*"); + typeRegEx.indexIn(type->minimalSignature()); + expr = typeRegEx.cap(1) + expr; + } + } else if (type->isContainer() && expr.contains('<')) { + QRegExp typeRegEx("[^<]*<(.*)>"); + typeRegEx.indexIn(type->minimalSignature()); + QRegExp defaultRegEx("([^<]*<).*(>[^>]*)"); + defaultRegEx.indexIn(expr); + expr = defaultRegEx.cap(1) + typeRegEx.cap(1) + defaultRegEx.cap(2); + } else { + // Here the default value is supposed to be a constructor, + // a class field, or a constructor receiving a class field + QRegExp defaultRegEx("([^\\(]*\\(|)([^\\)]*)(\\)|)"); + defaultRegEx.indexIn(expr); + + QString defaultValueCtorName = defaultRegEx.cap(1); + if (defaultValueCtorName.endsWith('(')) + defaultValueCtorName.chop(1); + + // Fix the scope for constructor using the already + // resolved argument type as a reference. + // The following regular expression extracts any + // use of namespaces/scopes from the type string. + QRegExp typeRegEx("^(?:const[\\s]+|)([\\w:]*::|)([A-Za-z_]\\w*)\\s*[&\\*]?$"); + typeRegEx.indexIn(type->minimalSignature()); + + QString typeNamespace = typeRegEx.cap(1); + QString typeCtorName = typeRegEx.cap(2); + if (!typeNamespace.isEmpty() && defaultValueCtorName == typeCtorName) + expr.prepend(typeNamespace); + + // Fix scope if the parameter is a field of the current class + foreach (const AbstractMetaField* field, implementingClass->fields()) { + if (defaultRegEx.cap(2) == field->name()) { + expr = defaultRegEx.cap(1) + implementingClass->name() + "::" + defaultRegEx.cap(2) + defaultRegEx.cap(3); + break; + } + } + } + } else { + QString warn = QString("undefined type for default value '%3' of argument in function '%1', class '%2'") + .arg(functionName).arg(className).arg(item->defaultValueExpression()); + ReportHandler::warning(warn); + expr = QString(); + } + + return expr; +} + +bool AbstractMetaBuilder::isQObject(const QString &qualifiedName) +{ + if (qualifiedName == "QObject") + return true; + + ClassModelItem classItem = m_dom->findClass(qualifiedName); + + if (!classItem) { + QStringList names = qualifiedName.split(QLatin1String("::")); + NamespaceModelItem ns = model_dynamic_cast(m_dom); + for (int i = 0; i < names.size() - 1 && ns; ++i) + ns = ns->namespaceMap().value(names.at(i)); + if (ns && names.size() >= 2) + classItem = ns->findClass(names.at(names.size() - 1)); + } + + bool isqobject = classItem && classItem->extendsClass("QObject"); + + if (classItem && !isqobject) { + QStringList baseClasses = classItem->baseClasses(); + for (int i = 0; i < baseClasses.count(); ++i) { + + isqobject = isQObject(baseClasses.at(i)); + if (isqobject) + break; + } + } + + return isqobject; +} + + +bool AbstractMetaBuilder::isEnum(const QStringList &qualified_name) +{ + CodeModelItem item = m_dom->model()->findItem(qualified_name, m_dom->toItem()); + return item && item->kind() == _EnumModelItem::__node_kind; +} + +AbstractMetaType *AbstractMetaBuilder::inheritTemplateType(const QList &templateTypes, + AbstractMetaType *metaType, bool *ok) +{ + if (ok) + *ok = true; + if (!metaType || (!metaType->typeEntry()->isTemplateArgument() && !metaType->hasInstantiations())) + return metaType ? metaType->copy() : 0; + + AbstractMetaType *returned = metaType->copy(); + returned->setOriginalTemplateType(metaType->copy()); + + if (returned->typeEntry()->isTemplateArgument()) { + const TemplateArgumentEntry *tae = static_cast(returned->typeEntry()); + + // If the template is intantiated with void we special case this as rejecting the functions that use this + // parameter from the instantiation. + if (templateTypes.size() <= tae->ordinal() || templateTypes.at(tae->ordinal())->typeEntry()->name() == "void") { + if (ok) + *ok = false; + return 0; + } + + AbstractMetaType *t = returned->copy(); + t->setTypeEntry(templateTypes.at(tae->ordinal())->typeEntry()); + t->setIndirections(templateTypes.at(tae->ordinal())->indirections() + t->indirections() ? 1 : 0); + decideUsagePattern(t); + + delete returned; + returned = inheritTemplateType(templateTypes, t, ok); + if (ok && !(*ok)) + return 0; + } + + if (returned->hasInstantiations()) { + QList instantiations = returned->instantiations(); + for (int i = 0; i < instantiations.count(); ++i) { + instantiations[i] = inheritTemplateType(templateTypes, instantiations.at(i), ok); + if (ok && !(*ok)) + return 0; + } + returned->setInstantiations(instantiations); + } + + return returned; +} + +bool AbstractMetaBuilder::inheritTemplate(AbstractMetaClass *subclass, + const AbstractMetaClass *templateClass, + const TypeParser::Info &info) +{ + QList targs = info.template_instantiations; + + QList templateTypes; + foreach (const TypeParser::Info &i, targs) { + TypeEntry *t = TypeDatabase::instance()->findType(i.qualified_name.join("::")); + + if (t) { + AbstractMetaType *temporaryType = createMetaType(); + temporaryType->setTypeEntry(t); + temporaryType->setConstant(i.is_constant); + temporaryType->setReference(i.is_reference); + temporaryType->setIndirections(i.indirections); + templateTypes << temporaryType; + } + } + + AbstractMetaFunctionList funcs = subclass->functions(); + foreach (const AbstractMetaFunction *function, templateClass->functions()) { + + if (function->isModifiedRemoved(TypeSystem::All)) + continue; + + AbstractMetaFunction *f = function->copy(); + f->setArguments(AbstractMetaArgumentList()); + + bool ok = true; + AbstractMetaType *ftype = function->type(); + f->setType(inheritTemplateType(templateTypes, ftype, &ok)); + if (!ok) { + delete f; + continue; + } + + foreach (AbstractMetaArgument *argument, function->arguments()) { + AbstractMetaType *atype = argument->type(); + + AbstractMetaArgument *arg = argument->copy(); + arg->setType(inheritTemplateType(templateTypes, atype, &ok)); + if (!ok) + break; + f->addArgument(arg); + } + + if (!ok) { + delete f; + continue; + } + + // There is no base class in the target language to inherit from here, so + // the template instantiation is the class that implements the function. + f->setImplementingClass(subclass); + + // We also set it as the declaring class, since the superclass is + // supposed to disappear. This allows us to make certain function modifications + // on the inherited functions. + f->setDeclaringClass(subclass); + + + if (f->isConstructor() && subclass->isTypeAlias()) { + f->setName(subclass->name()); + } else if (f->isConstructor()) { + delete f; + continue; + } + + // if the instantiation has a function named the same as an existing + // function we have shadowing so we need to skip it. + bool found = false; + for (int i = 0; i < funcs.size(); ++i) { + if (funcs.at(i)->name() == f->name()) { + found = true; + continue; + } + } + if (found) { + delete f; + continue; + } + + ComplexTypeEntry *te = subclass->typeEntry(); + FunctionModificationList mods = function->modifications(templateClass); + for (int i = 0; i < mods.size(); ++i) { + FunctionModification mod = mods.at(i); + mod.signature = f->minimalSignature(); + + // If we ever need it... Below is the code to do + // substitution of the template instantation type inside + // injected code.. +#if 0 + if (mod.modifiers & Modification::CodeInjection) { + for (int j = 0; j < template_types.size(); ++j) { + CodeSnip &snip = mod.snips.last(); + QString code = snip.code(); + code.replace(QString::fromLatin1("$$QT_TEMPLATE_%1$$").arg(j), + template_types.at(j)->typeEntry()->qualifiedCppName()); + snip.codeList.clear(); + snip.addCode(code); + } + } +#endif + te->addFunctionModification(mod); + } + + subclass->addFunction(f); + } + + // Clean up + foreach (AbstractMetaType *type, templateTypes) + delete type; + + subclass->setTemplateBaseClass(templateClass); + subclass->setInterfaces(templateClass->interfaces()); + subclass->setBaseClass(templateClass->baseClass()); + + return true; +} + +void AbstractMetaBuilder::parseQ_Property(AbstractMetaClass *metaClass, const QStringList &declarations) +{ + for (int i = 0; i < declarations.size(); ++i) { + QString p = declarations.at(i); + + QStringList l = p.split(QLatin1String(" ")); + + + QStringList qualifiedScopeName = currentScope()->qualifiedName(); + bool ok = false; + AbstractMetaType *type = 0; + QString scope; + for (int j = qualifiedScopeName.size(); j >= 0; --j) { + scope = j > 0 ? QStringList(qualifiedScopeName.mid(0, j)).join("::") + "::" : QString(); + TypeInfo info; + info.setQualifiedName((scope + l.at(0)).split("::")); + + type = translateType(info, &ok); + if (type && ok) + break; + } + + if (!type || !ok) { + ReportHandler::warning(QString("Unable to decide type of property: '%1' in class '%2'") + .arg(l.at(0)).arg(metaClass->name())); + continue; + } + + QString typeName = scope + l.at(0); + + QPropertySpec *spec = new QPropertySpec(type->typeEntry()); + spec->setName(l.at(1)); + spec->setIndex(i); + + for (int pos = 2; pos + 1 < l.size(); pos += 2) { + if (l.at(pos) == QLatin1String("READ")) + spec->setRead(l.at(pos + 1)); + else if (l.at(pos) == QLatin1String("WRITE")) + spec->setWrite(l.at(pos + 1)); + else if (l.at(pos) == QLatin1String("DESIGNABLE")) + spec->setDesignable(l.at(pos + 1)); + else if (l.at(pos) == QLatin1String("RESET")) + spec->setReset(l.at(pos + 1)); + } + + metaClass->addPropertySpec(spec); + delete type; + } +} + +#if 0 +static void hide_functions(const AbstractMetaFunctionList &l) +{ + foreach (AbstractMetaFunction *f, l) { + FunctionModification mod; + mod.signature = f->minimalSignature(); + mod.modifiers = FunctionModification::Private; + ((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod); + } +} + +static void remove_function(AbstractMetaFunction *f) +{ + FunctionModification mod; + mod.removal = TypeSystem::All; + mod.signature = f->minimalSignature(); + ((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod); +} + +static AbstractMetaFunctionList filter_functions(const AbstractMetaFunctionList &lst, QSet *signatures) +{ + AbstractMetaFunctionList functions; + foreach (AbstractMetaFunction *f, lst) { + QString signature = f->minimalSignature(); + int start = signature.indexOf(QLatin1Char('(')) + 1; + int end = signature.lastIndexOf(QLatin1Char(')')); + signature = signature.mid(start, end - start); + if (signatures->contains(signature)) { + remove_function(f); + continue; + } + (*signatures) << signature; + functions << f; + } + return functions; +} + +void AbstractMetaBuilder::setupEquals(AbstractMetaClass */*cls*/) +{ +// python have operator overloading, so we need all operators declared in C++. + AbstractMetaFunctionList equals; + AbstractMetaFunctionList nequals; + + QString op_equals = QLatin1String("operator_equal"); + QString opNequals = QLatin1String("operator_not_equal"); + + AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements + | AbstractMetaClass::NotRemovedFromTargetLang); + foreach (AbstractMetaFunction *f, functions) { + if (f->name() == op_equals) + equals << f; + else if (f->name() == opNequals) + nequals << f; + } + + if (equals.size() || nequals.size()) { + if (!cls->hasHashFunction()) { + ReportHandler::warning(QString::fromLatin1("Class '%1' has equals operators but no qHash() function") + .arg(cls->name())); + } + + hide_functions(equals); + hide_functions(nequals); + + // We only need == if we have both == and !=, and one == for + // each signature type, like QDateTime::==(QDate) and (QTime) + // if such a thing exists... + QSet func_signatures; + cls->setEqualsFunctions(filter_functions(equals, &func_signatures)); + cls->setNotEqualsFunctions(filter_functions(nequals, &func_signatures)); + } +} + +void AbstractMetaBuilder::setupComparable(AbstractMetaClass *cls) +{ + AbstractMetaFunctionList greater; + AbstractMetaFunctionList greaterEquals; + AbstractMetaFunctionList less; + AbstractMetaFunctionList lessEquals; + + QString op_greater = QLatin1String("operator_greater"); + QString opGreaterEq = QLatin1String("operator_greater_or_equal"); + QString op_less = QLatin1String("operator_less"); + QString opLessEq = QLatin1String("operator_less_or_equal"); + + AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements + | AbstractMetaClass::NotRemovedFromTargetLang); + foreach (AbstractMetaFunction *f, functions) { + if (f->name() == op_greater) + greater << f; + else if (f->name() == opGreaterEq) + greaterEquals << f; + else if (f->name() == op_less) + less << f; + else if (f->name() == opLessEq) + lessEquals << f; + } + + bool hasEquals = cls->equalsFunctions().size() || cls->notEqualsFunctions().size(); + + // Conditions for comparable is: + // >, ==, < - The basic case + // >, == - Less than becomes else case + // <, == - Greater than becomes else case + // >=, <= - if (<= && >=) -> equal + bool mightBeComparable = greater.size() || greaterEquals.size() || less.size() || lessEquals.size() + || greaterEquals.size() == 1 || lessEquals.size() == 1; + + if (mightBeComparable) { + QSet signatures; + + // We only hide the original functions if we are able to make a compareTo() method + bool wasComparable = false; + + // The three upper cases, prefer the <, == approach + if (hasEquals && (greater.size() || less.size())) { + cls->setLessThanFunctions(filter_functions(less, &signatures)); + cls->setGreaterThanFunctions(filter_functions(greater, &signatures)); + filter_functions(greaterEquals, &signatures); + filter_functions(lessEquals, &signatures); + wasComparable = true; + } else if (hasEquals && (greaterEquals.size() || lessEquals.size())) { + cls->setLessThanEqFunctions(filter_functions(lessEquals, &signatures)); + cls->setGreaterThanEqFunctions(filter_functions(greaterEquals, &signatures)); + wasComparable = true; + } else if (greaterEquals.size() == 1 || lessEquals.size() == 1) { + cls->setGreaterThanEqFunctions(greaterEquals); + cls->setLessThanEqFunctions(lessEquals); + filter_functions(less, &signatures); + filter_functions(greater, &signatures); + wasComparable = true; + } + + if (wasComparable) { + hide_functions(greater); + hide_functions(greaterEquals); + hide_functions(less); + hide_functions(lessEquals); + } + } + +} +#endif + +static AbstractMetaFunction *findCopyCtor(AbstractMetaClass *cls) +{ + AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::Invisible); + functions << cls->queryFunctions(AbstractMetaClass::Visible); + + foreach (AbstractMetaFunction *f, functions) { + if (f->isConstructor() || f->name() == "operator=") { + AbstractMetaArgumentList arguments = f->arguments(); + if (arguments.size() == 1) { + if (cls->typeEntry()->qualifiedCppName() == arguments.at(0)->type()->typeEntry()->qualifiedCppName()) + return f; + } + } + } + return 0; +} + +void AbstractMetaBuilder::setupClonable(AbstractMetaClass *cls) +{ + bool result = true; + + // find copy ctor for the current class + AbstractMetaFunction *copyCtor = findCopyCtor(cls); + if (copyCtor) { // if exists a copy ctor in this class + result = copyCtor->isPublic(); + } else { // else... lets find one in the parent class + QQueue baseClasses; + if (cls->baseClass()) + baseClasses.enqueue(cls->baseClass()); + baseClasses << cls->interfaces(); + + while (!baseClasses.isEmpty()) { + AbstractMetaClass* currentClass = baseClasses.dequeue(); + baseClasses << currentClass->interfaces(); + if (currentClass->baseClass()) + baseClasses.enqueue(currentClass->baseClass()); + + copyCtor = findCopyCtor(currentClass); + if (copyCtor) { + result = copyCtor->isPublic(); + break; + } + } + } + cls->setHasCloneOperator(result); +} + +static void writeRejectLogFile(const QString &name, + const QMap &rejects) +{ + QFile f(name); + if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { + ReportHandler::warning(QString("failed to write log file: '%1'") + .arg(f.fileName())); + return; + } + + QTextStream s(&f); + + + for (int reason = 0; reason < AbstractMetaBuilder::NoReason; ++reason) { + s << QString(72, '*') << endl; + switch (reason) { + case AbstractMetaBuilder::NotInTypeSystem: + s << "Not in type system"; + break; + case AbstractMetaBuilder::GenerationDisabled: + s << "Generation disabled by type system"; + break; + case AbstractMetaBuilder::RedefinedToNotClass: + s << "Type redefined to not be a class"; + break; + + case AbstractMetaBuilder::UnmatchedReturnType: + s << "Unmatched return type"; + break; + + case AbstractMetaBuilder::UnmatchedArgumentType: + s << "Unmatched argument type"; + break; + + default: + s << "unknown reason"; + break; + } + + s << endl; + + for (QMap::const_iterator it = rejects.constBegin(); + it != rejects.constEnd(); ++it) { + if (it.value() != reason) + continue; + s << " - " << it.key() << endl; + } + + s << QString(72, '*') << endl << endl; + } + +} + + +void AbstractMetaBuilder::dumpLog() +{ + writeRejectLogFile("mjb_rejected_classes.log", m_rejectedClasses); + writeRejectLogFile("mjb_rejected_enums.log", m_rejectedEnums); + writeRejectLogFile("mjb_rejected_functions.log", m_rejectedFunctions); + writeRejectLogFile("mjb_rejected_fields.log", m_rejectedFields); +} + +AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted(const AbstractMetaClass* cppClass) const +{ + using namespace boost; + + AbstractMetaClassList result; + QList unmappedResult; + QSet > deps; + QHash map; + QHash reverseMap; + + AbstractMetaClassList classList; + if (cppClass) + classList = cppClass->innerClasses(); + else + classList = m_metaClasses; + + int i = 0; + foreach (AbstractMetaClass* clazz, classList) { + map[clazz->name()] = i; + reverseMap[i] = clazz; + i++; + } + + // TODO choose a better name to these regexs + QRegExp regex1("\\(.*\\)"); + QRegExp regex2("::.*"); + foreach (AbstractMetaClass* clazz, classList) { + if (clazz->isInterface() || !clazz->typeEntry()->generateCode()) + continue; + + // check base class dep. + QString baseClassName(clazz->baseClassName()); + if (!baseClassName.isNull() && baseClassName != clazz->name() && map.contains(baseClassName)) { + if (clazz->baseClass()->enclosingClass() && + clazz->baseClass()->enclosingClass() != clazz->enclosingClass()) { + baseClassName = clazz->baseClass()->enclosingClass()->name(); + } + deps << qMakePair(map[clazz->name()], map[baseClassName]); + } + + // interfaces... + foreach (AbstractMetaClass* interface, clazz->interfaces()) { + if (!interface->typeEntry()->generateCode()) + continue; + + if (interface->isInterface()) + interface = interface->primaryInterfaceImplementor(); + + if (interface->enclosingClass() && + interface->enclosingClass() != clazz->enclosingClass()) { + baseClassName = interface->enclosingClass()->name(); + } else { + baseClassName = interface->name(); + } + + if (!baseClassName.isNull() && baseClassName != clazz->name() && map.contains(baseClassName)) + deps << qMakePair(map[clazz->name()], map[baseClassName]); + } + + foreach (AbstractMetaFunction* func, clazz->functions()) { + foreach (AbstractMetaArgument* arg, func->arguments()) { + // check methods with default args + QString defaultExpression = arg->originalDefaultValueExpression(); + if (!defaultExpression.isEmpty()) { + defaultExpression.replace(regex1, ""); + defaultExpression.replace(regex2, ""); + } + if (!defaultExpression.isEmpty() && defaultExpression != clazz->name() && map.contains(defaultExpression)) + deps << qMakePair(map[clazz->name()], map[defaultExpression]); + } + } + } + + // dot output for debug. +// typedef QPair ABC; +// qDebug() << "digraph G { "; +// foreach (ABC p, deps) { +// TypeEntry* typeEntry = TypeDatabase::instance()->findType(reverseMap[p.second]->name()); +// if (typeEntry && !typeEntry->generateCode()) +// continue; +// qDebug() << reverseMap[p.first]->name() << " -> " << reverseMap[p.second]->name(); +// } +// qDebug() << "}"; + + + typedef adjacency_list Graph; + Graph g(deps.begin(), deps.end(), classList.count()); + topological_sort(g, std::back_inserter(unmappedResult)); + + foreach (int i, unmappedResult) { + Q_ASSERT(reverseMap.contains(i)); + if (!reverseMap[i]->isInterface()) + result << reverseMap[i]; + } + + return result; +} + + +AbstractMetaArgumentList AbstractMetaBuilder::reverseList(const AbstractMetaArgumentList& list) +{ + AbstractMetaArgumentList ret; + + int index = list.size(); + foreach (AbstractMetaArgument *arg, list) { + arg->setArgumentIndex(index); + ret.prepend(arg); + index--; + } + + return ret; +} diff --git a/abstractmetabuilder.h b/abstractmetabuilder.h new file mode 100644 index 000000000..3cc5f3cee --- /dev/null +++ b/abstractmetabuilder.h @@ -0,0 +1,225 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef ABSTRACTMETABUILDER_H +#define ABSTRACTMETABUILDER_H + +#include "parser/codemodel.h" +#include "abstractmetalang.h" +#include "typesystem.h" +#include "typeparser.h" + +#include + +class AbstractMetaBuilder +{ +public: + enum RejectReason { + NotInTypeSystem, + GenerationDisabled, + RedefinedToNotClass, + UnmatchedArgumentType, + UnmatchedReturnType, + NoReason + }; + + AbstractMetaBuilder(); + virtual ~AbstractMetaBuilder(); + + AbstractMetaClassList classes() const + { + return m_metaClasses; + } + /** + * Sorts a list of classes topologically, if an AbstractMetaClass object + * is passed the list of classes will be its inner classes, otherwise + * the list will be the module global classes. + * \return a list of classes sorted topologically + */ + AbstractMetaClassList classesTopologicalSorted(const AbstractMetaClass* cppClass = 0) const; + + FileModelItem model() const + { + return m_dom; + } + + void setModel(FileModelItem item) + { + m_dom = item; + } + + ScopeModelItem popScope() + { + return m_scopes.takeLast(); + } + + void pushScope(ScopeModelItem item) + { + m_scopes << item; + } + + ScopeModelItem currentScope() const + { + return m_scopes.last(); + } + + void dumpLog(); + + bool build(QIODevice* input); + + void figureOutEnumValuesForClass(AbstractMetaClass *metaClass, QSet *classes); + int figureOutEnumValue(const QString &name, int value, AbstractMetaEnum *meta_enum, AbstractMetaFunction *metaFunction = 0); + void figureOutEnumValues(); + void figureOutDefaultEnumArguments(); + + void addAbstractMetaClass(AbstractMetaClass *cls); + AbstractMetaClass *traverseTypeAlias(TypeAliasModelItem item); + AbstractMetaClass *traverseClass(ClassModelItem item); + bool setupInheritance(AbstractMetaClass *metaClass); + AbstractMetaClass *traverseNamespace(NamespaceModelItem item); + AbstractMetaEnum *traverseEnum(EnumModelItem item, AbstractMetaClass *enclosing, const QSet &enumsDeclarations); + void traverseEnums(ScopeModelItem item, AbstractMetaClass *parent, const QStringList &enumsDeclarations); + void traverseFunctions(ScopeModelItem item, AbstractMetaClass *parent); + void applyFunctionModifications(AbstractMetaFunction* func); + void traverseFields(ScopeModelItem item, AbstractMetaClass *parent); + void traverseStreamOperator(FunctionModelItem functionItem); + void traverseOperatorFunction(FunctionModelItem item); + AbstractMetaFunction *traverseFunction(FunctionModelItem function); + AbstractMetaField *traverseField(VariableModelItem field, const AbstractMetaClass *cls); + void checkFunctionModifications(); + void registerHashFunction(FunctionModelItem functionItem); + void registerToStringCapability(FunctionModelItem functionItem); + + void parseQ_Property(AbstractMetaClass *metaClass, const QStringList &declarations); + void setupEquals(AbstractMetaClass *metaClass); + void setupComparable(AbstractMetaClass *metaClass); + void setupClonable(AbstractMetaClass *cls); + void setupFunctionDefaults(AbstractMetaFunction *metaFunction, AbstractMetaClass *metaClass); + + QString translateDefaultValue(ArgumentModelItem item, AbstractMetaType *type, + AbstractMetaFunction *fnc, AbstractMetaClass *, + int argumentIndex); + AbstractMetaType *translateType(const TypeInfo &type, bool *ok, bool resolveType = true, bool resolveScope = true); + + void decideUsagePattern(AbstractMetaType *type); + + const AbstractMetaFunctionList globalFunctions() const + { + return m_globalFunctions; + } + + const AbstractMetaEnumList globalEnums() const + { + return m_globalEnums; + } + + bool inheritTemplate(AbstractMetaClass *subclass, + const AbstractMetaClass *templateClass, + const TypeParser::Info &info); + AbstractMetaType *inheritTemplateType(const QList &templateTypes, AbstractMetaType *metaType, bool *ok = 0); + + bool isQObject(const QString &qualifiedName); + bool isEnum(const QStringList &qualifiedName); + + void fixQObjectForScope(TypeDatabase *types, + NamespaceModelItem item); + + // QtScript + QSet qtMetaTypeDeclaredTypeNames() const + { + return m_qmetatypeDeclaredTypenames; + } + +protected: + AbstractMetaClass *argumentToClass(ArgumentModelItem); + + virtual AbstractMetaClass *createMetaClass() + { + return new AbstractMetaClass(); + } + + virtual AbstractMetaEnum *createMetaEnum() + { + return new AbstractMetaEnum(); + } + + virtual AbstractMetaEnumValue *createMetaEnumValue() + { + return new AbstractMetaEnumValue(); + } + + virtual AbstractMetaField *createMetaField() + { + return new AbstractMetaField(); + } + + virtual AbstractMetaFunction *createMetaFunction() + { + return new AbstractMetaFunction(); + } + + virtual AbstractMetaArgument *createMetaArgument() + { + return new AbstractMetaArgument(); + } + + virtual AbstractMetaType *createMetaType() + { + return new AbstractMetaType(); + } + + FileModelItem m_dom; + +private: + void sortLists(); + AbstractMetaArgumentList reverseList(const AbstractMetaArgumentList& list); + + AbstractMetaClassList m_metaClasses; + AbstractMetaClassList m_templates; + AbstractMetaFunctionList m_globalFunctions; + AbstractMetaEnumList m_globalEnums; + + QSet m_usedTypes; + + QMap m_rejectedClasses; + QMap m_rejectedEnums; + QMap m_rejectedFunctions; + QMap m_rejectedFields; + + QList m_enums; + + QList > m_enumDefaultArguments; + + QHash m_enumValues; + + AbstractMetaClass *m_currentClass; + QList m_scopes; + QString m_namespacePrefix; + + QSet m_setupInheritanceDone; + + // QtScript + QSet m_qmetatypeDeclaredTypenames; +}; + +#endif // ABSTRACTMETBUILDER_H diff --git a/abstractmetalang.cpp b/abstractmetalang.cpp new file mode 100644 index 000000000..a789b9eb6 --- /dev/null +++ b/abstractmetalang.cpp @@ -0,0 +1,2175 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "abstractmetalang.h" +#include "reporthandler.h" + +/******************************************************************************* + * AbstractMetaType + */ +AbstractMetaType *AbstractMetaType::copy() const +{ + AbstractMetaType *cpy = new AbstractMetaType; + + cpy->setTypeUsagePattern(typeUsagePattern()); + cpy->setConstant(isConstant()); + cpy->setReference(isReference()); + cpy->setIndirections(indirections()); + cpy->setInstantiations(instantiations()); + cpy->setArrayElementCount(arrayElementCount()); + cpy->setOriginalTypeDescription(originalTypeDescription()); + cpy->setOriginalTemplateType(originalTemplateType() ? originalTemplateType()->copy() : 0); + + cpy->setArrayElementType(arrayElementType() ? arrayElementType()->copy() : 0); + + cpy->setTypeEntry(typeEntry()); + + return cpy; +} + +QString AbstractMetaType::cppSignature() const +{ + QString s; + + if (isConstant()) + s += "const "; + + s += typeEntry()->qualifiedCppName(); + + if (hasInstantiationInCpp()) { + QList types = instantiations(); + s += "<"; + for (int i = 0; i < types.count(); ++i) { + if (i > 0) + s += ", "; + s += types.at(i)->cppSignature(); + } + s += " >"; + } + + if (actualIndirections()) { + s += ' '; + if (indirections()) + s += QString(indirections(), '*'); + if (isReference()) + s += '&'; + } + return s; +} + +/******************************************************************************* + * AbstractMetaArgument + */ +AbstractMetaArgument *AbstractMetaArgument::copy() const +{ + AbstractMetaArgument *cpy = new AbstractMetaArgument; + cpy->setName(AbstractMetaVariable::name()); + cpy->setDefaultValueExpression(defaultValueExpression()); + cpy->setOriginalDefaultValueExpression(originalDefaultValueExpression()); + cpy->setType(type()->copy()); + cpy->setArgumentIndex(argumentIndex()); + + return cpy; +} + + +QString AbstractMetaArgument::argumentName() const +{ + QString n = AbstractMetaVariable::name(); + if (n.isEmpty()) + return QString("arg__%2").arg(m_argumentIndex + 1); + return n; +} + + +QString AbstractMetaArgument::indexedName() const +{ + QString n = AbstractMetaVariable::name(); + if (n.isEmpty()) + return argumentName(); + return QString("%1%2").arg(n).arg(m_argumentIndex); +} + +QString AbstractMetaArgument::name() const +{ + Q_ASSERT_X(0, "AbstractMetaArgument::name()", "use argumentName() or indexedName() instead"); + return QString(); +} + + +/******************************************************************************* + * AbstractMetaFunction + */ +AbstractMetaFunction::~AbstractMetaFunction() +{ + qDeleteAll(m_arguments); + delete m_type; +} + +/******************************************************************************* + * Indicates that this function has a modification that removes it + */ +bool AbstractMetaFunction::isModifiedRemoved(int types) const +{ + FunctionModificationList mods = modifications(implementingClass()); + foreach (FunctionModification mod, mods) { + if (!mod.isRemoveModifier()) + continue; + + if ((mod.removal & types) == types) + return true; + } + + return false; +} + +bool AbstractMetaFunction::needsCallThrough() const +{ + if (ownerClass()->isInterface()) + return false; + if (referenceCounts(implementingClass()).size() > 0) + return true; + if (argumentsHaveNativeId() || !isStatic()) + return true; + + foreach (const AbstractMetaArgument *arg, arguments()) { + if (arg->type()->isArray() || arg->type()->isTargetLangEnum() || arg->type()->isTargetLangFlags()) + return true; + } + + if (type() && (type()->isArray() || type()->isTargetLangEnum() || type()->isTargetLangFlags())) + return true; + + for (int i = -1; i <= arguments().size(); ++i) { + TypeSystem::Ownership owner = this->ownership(implementingClass(), TypeSystem::TargetLangCode, i); + if (owner != TypeSystem::InvalidOwnership) + return true; + } + + return false; +} + +bool AbstractMetaFunction::needsSuppressUncheckedWarning() const +{ + for (int i = -1; i <= arguments().size(); ++i) { + QList referenceCounts = this->referenceCounts(implementingClass(), i); + foreach (ReferenceCount referenceCount, referenceCounts) { + if (referenceCount.action != ReferenceCount::Set) + return true; + } + } + return false; +} + +QString AbstractMetaFunction::marshalledName() const +{ + QString returned = "__qt_" + name(); + AbstractMetaArgumentList arguments = this->arguments(); + foreach (const AbstractMetaArgument *arg, arguments) { + returned += "_"; + if (arg->type()->isNativePointer()) + returned += "nativepointer"; + else if (arg->type()->isIntegerEnum() || arg->type()->isIntegerFlags()) + returned += "int"; + else + returned += arg->type()->name().replace("[]", "_3").replace(".", "_"); + } + return returned; +} + +bool AbstractMetaFunction::operator<(const AbstractMetaFunction &other) const +{ + uint result = compareTo(&other); + return result & NameLessThan; +} + + +/*! + Returns a mask of CompareResult describing how this function is + compares to another function +*/ +uint AbstractMetaFunction::compareTo(const AbstractMetaFunction *other) const +{ + uint result = 0; + + // Enclosing class... + if (ownerClass() == other->ownerClass()) + result |= EqualImplementor; + + // Attributes + if (attributes() == other->attributes()) + result |= EqualAttributes; + + // Compare types + AbstractMetaType *t = type(); + AbstractMetaType *ot = other->type(); + if ((!t && !ot) || ((t && ot && t->name() == ot->name()))) + result |= EqualReturnType; + + // Compare names + int cmp = originalName().compare(other->originalName()); + + if (cmp < 0) + result |= NameLessThan; + else if (!cmp) + result |= EqualName; + + // compare name after modification... + cmp = modifiedName().compare(other->modifiedName()); + if (!cmp) + result |= EqualModifiedName; + + // Compare arguments... + AbstractMetaArgumentList minArguments; + AbstractMetaArgumentList maxArguments; + if (arguments().size() < other->arguments().size()) { + minArguments = arguments(); + maxArguments = other->arguments(); + } else { + minArguments = other->arguments(); + maxArguments = arguments(); + } + + int minCount = minArguments.size(); + int maxCount = maxArguments.size(); + bool same = true; + for (int i = 0; i < maxCount; ++i) { + if (i < minCount) { + const AbstractMetaArgument *min_arg = minArguments.at(i); + const AbstractMetaArgument *max_arg = maxArguments.at(i); + if (min_arg->type()->name() != max_arg->type()->name() + && (min_arg->defaultValueExpression().isEmpty() || max_arg->defaultValueExpression().isEmpty())) { + same = false; + break; + } + } else { + if (maxArguments.at(i)->defaultValueExpression().isEmpty()) { + same = false; + break; + } + } + } + + if (same) + result |= minCount == maxCount ? EqualArguments : EqualDefaultValueOverload; + + return result; +} + +AbstractMetaFunction *AbstractMetaFunction::copy() const +{ + AbstractMetaFunction *cpy = new AbstractMetaFunction; + cpy->setName(name()); + cpy->setOriginalName(originalName()); + cpy->setOwnerClass(ownerClass()); + cpy->setImplementingClass(implementingClass()); + cpy->setInterfaceClass(interfaceClass()); + cpy->setFunctionType(functionType()); + cpy->setAttributes(attributes()); + cpy->setDeclaringClass(declaringClass()); + if (type()) + cpy->setType(type()->copy()); + cpy->setConstant(isConstant()); + cpy->setOriginalAttributes(originalAttributes()); + + foreach (AbstractMetaArgument *arg, arguments()) + cpy->addArgument(arg->copy()); + + Q_ASSERT((!type() && !cpy->type()) + || (type()->instantiations() == cpy->type()->instantiations())); + + return cpy; +} + +QStringList AbstractMetaFunction::introspectionCompatibleSignatures(const QStringList &resolvedArguments) const +{ + AbstractMetaArgumentList arguments = this->arguments(); + if (arguments.size() == resolvedArguments.size()) { + return (QStringList() << QMetaObject::normalizedSignature((name() + "(" + resolvedArguments.join(",") + ")").toUtf8().constData())); + } else { + QStringList returned; + + AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); + QStringList minimalTypeSignature = argument->type()->minimalSignature().split("::"); + for (int i = 0; i < minimalTypeSignature.size(); ++i) { + returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) + << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join("::")); + } + + return returned; + } +} + +QString AbstractMetaFunction::signature() const +{ + QString s(m_originalName); + + s += "("; + + for (int i = 0; i < m_arguments.count(); ++i) { + if (i > 0) + s += ", "; + AbstractMetaArgument *a = m_arguments.at(i); + s += a->type()->cppSignature(); + + // We need to have the argument names in the qdoc files + s += " "; + s += a->argumentName(); + } + s += ")"; + + if (isConstant()) + s += " const"; + + return s; +} + +int AbstractMetaFunction::actualMinimumArgumentCount() const +{ + AbstractMetaArgumentList arguments = this->arguments(); + + int count = 0; + for (int i = 0; i < arguments.size(); ++i && ++count) { + if (argumentRemoved(i + 1)) + --count; + else if (!arguments.at(i)->defaultValueExpression().isEmpty()) + break; + } + + return count; +} + +// Returns reference counts for argument at idx, or all arguments if idx == -2 +QList AbstractMetaFunction::referenceCounts(const AbstractMetaClass *cls, int idx) const +{ + QList returned; + + FunctionModificationList mods = this->modifications(cls); + foreach (FunctionModification mod, mods) { + QList argumentMods = mod.argument_mods; + foreach (ArgumentModification argumentMod, argumentMods) { + if (argumentMod.index != idx && idx != -2) + continue; + returned += argumentMod.referenceCounts; + } + } + + return returned; +} + + +ArgumentOwner AbstractMetaFunction::argumentOwner(const AbstractMetaClass *cls, int idx) const +{ + FunctionModificationList mods = this->modifications(cls); + foreach (FunctionModification mod, mods) { + QList argumentMods = mod.argument_mods; + foreach (ArgumentModification argumentMod, argumentMods) { + if (argumentMod.index != idx) + continue; + return argumentMod.owner; + } + } + return ArgumentOwner(); +} + + +QString AbstractMetaFunction::replacedDefaultExpression(const AbstractMetaClass *cls, int key) const +{ + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == key + && !argumentModification.replacedDefaultExpression.isEmpty()) { + return argumentModification.replacedDefaultExpression; + } + } + } + + return QString(); +} + +bool AbstractMetaFunction::removedDefaultExpression(const AbstractMetaClass *cls, int key) const +{ + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == key + && argumentModification.removedDefaultExpression) { + return true; + } + } + } + + return false; +} + +bool AbstractMetaFunction::resetObjectAfterUse(int argumentIdx) const +{ + const AbstractMetaClass *cls = declaringClass(); + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == argumentIdx && argumentModification.resetAfterUse) + return true; + } + } + + return false; +} + +QString AbstractMetaFunction::nullPointerDefaultValue(const AbstractMetaClass *mainClass, int argumentIdx) const +{ + Q_ASSERT(nullPointersDisabled(mainClass, argumentIdx)); + + const AbstractMetaClass *cls = mainClass; + if (!cls) + cls = implementingClass(); + + do { + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == argumentIdx + && argumentModification.noNullPointers) { + return argumentModification.nullPointerDefaultValue; + } + } + } + cls = cls->baseClass(); + } while (cls && !mainClass); // Once when mainClass, or once for all base classes of implementing class + + return QString(); + +} + +bool AbstractMetaFunction::nullPointersDisabled(const AbstractMetaClass *mainClass, int argumentIdx) const +{ + const AbstractMetaClass *cls = mainClass; + if (!cls) + cls = implementingClass(); + + do { + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == argumentIdx + && argumentModification.noNullPointers) { + return true; + } + } + } + + cls = cls->baseClass(); + } while (cls && !mainClass); // Once when mainClass, or once for all base classes of implementing class + + return false; +} + +QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index != key) + continue; + + foreach (CodeSnip snip, argumentModification.conversion_rules) { + if (snip.language == language && !snip.code().isEmpty()) + return snip.code(); + } + } + } + + return QString(); +} + +QString AbstractMetaFunction::argumentReplaced(int key) const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == key && !argumentModification.replace_value.isEmpty()) + return argumentModification.replace_value; + } + } + + return ""; +} + +// FIXME If we remove a arg. in the method at the base class, it will not reflect here. +bool AbstractMetaFunction::argumentRemoved(int key) const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == key) { + if (argumentModification.removed) + return true; + } + } + } + + return false; +} + +bool AbstractMetaFunction::isVirtualSlot() const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + if (modification.isVirtualSlot()) + return true; + } + + return false; +} + +bool AbstractMetaFunction::disabledGarbageCollection(const AbstractMetaClass *cls, int key) const +{ + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index != key) + continue; + + foreach (TypeSystem::Ownership ownership, argumentModification.ownerships.values()) { + if (ownership == TypeSystem::CppOwnership) + return true; + } + + } + } + + return false; +} + +bool AbstractMetaFunction::isDeprecated() const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + if (modification.isDeprecated()) + return true; + } + return false; +} + +bool AbstractMetaFunction::isThread() const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + if (modification.isThread()) + return true; + } + return false; +} + +bool AbstractMetaFunction::allowThread() const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + if (modification.allowThread()) + return true; + } + return false; +} + + +TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int key) const +{ + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == key) + return argumentModification.ownerships.value(language, TypeSystem::InvalidOwnership); + } + } + + return TypeSystem::InvalidOwnership; +} + +bool AbstractMetaFunction::isRemovedFromAllLanguages(const AbstractMetaClass *cls) const +{ + return isRemovedFrom(cls, TypeSystem::All); +} + +bool AbstractMetaFunction::isRemovedFrom(const AbstractMetaClass *cls, TypeSystem::Language language) const +{ + FunctionModificationList modifications = this->modifications(cls); + foreach (FunctionModification modification, modifications) { + if ((modification.removal & language) == language) + return true; + } + + return false; + +} + +QString AbstractMetaFunction::typeReplaced(int key) const +{ + FunctionModificationList modifications = this->modifications(declaringClass()); + foreach (FunctionModification modification, modifications) { + QList argumentModifications = modification.argument_mods; + foreach (ArgumentModification argumentModification, argumentModifications) { + if (argumentModification.index == key + && !argumentModification.modified_type.isEmpty()) { + return argumentModification.modified_type; + } + } + } + + return QString(); +} + +QString AbstractMetaFunction::minimalSignature() const +{ + if (!m_cachedMinimalSignature.isEmpty()) + return m_cachedMinimalSignature; + + QString minimalSignature = originalName() + "("; + AbstractMetaArgumentList arguments = this->arguments(); + + for (int i = 0; i < arguments.count(); ++i) { + AbstractMetaType *t = arguments.at(i)->type(); + + if (i > 0) + minimalSignature += ","; + + minimalSignature += t->minimalSignature(); + } + minimalSignature += ")"; + if (isConstant()) + minimalSignature += "const"; + + minimalSignature = QMetaObject::normalizedSignature(minimalSignature.toLocal8Bit().constData()); + m_cachedMinimalSignature = minimalSignature; + + return minimalSignature; +} + +FunctionModificationList AbstractMetaFunction::modifications(const AbstractMetaClass *implementor) const +{ + // FIXME Global functions do not support modifications + if (!implementor) + return FunctionModificationList(); + return implementor->typeEntry()->functionModifications(minimalSignature()); +} + +bool AbstractMetaFunction::hasModifications(const AbstractMetaClass *implementor) const +{ + return !modifications(implementor).isEmpty(); +} + +bool AbstractMetaFunction::hasInjectedCode() const +{ + foreach (const FunctionModification mod, modifications(ownerClass())) { + if (mod.isCodeInjection()) + return true; + } + return false; +} + +bool AbstractMetaFunction::hasSignatureModifications() const +{ + foreach (const FunctionModification mod, modifications(ownerClass())) { + foreach (const ArgumentModification argmod, mod.argument_mods) { + // since zero represents the return type and we're + // interested only in checking the function arguments, + // it will be ignored. + if (argmod.index > 0) + return true; + } + } + return false; +} + +bool AbstractMetaFunction::isConversionOperator(QString funcName) +{ + QRegExp opRegEx("^operator(?:\\s+(?:const|volatile))?\\s+(\\w+\\s*)&?$"); + return opRegEx.indexIn(funcName) > -1; +} + +bool AbstractMetaFunction::isOperatorOverload(QString funcName) +{ + if (isConversionOperator(funcName)) + return true; + + QRegExp opRegEx("^operator([+\\-\\*/%=&\\|\\^\\<>!][=]?" + "|\\+\\+|\\-\\-|&&|\\|\\||<<[=]?|>>[=]?|~" + "|\\[\\]|\\s+delete\\[?\\]?" + "|\\s+new\\[?\\]?)$"); + return opRegEx.indexIn(funcName) > -1; +} + +bool AbstractMetaFunction::isArithmeticOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == "operator+" || name == "operator+=" + || name == "operator-" || name == "operator-=" + || name == "operator*" || name == "operator*=" + || name == "operator/" || name == "operator/=" + || name == "operator%" || name == "operator%=" + || name == "operator++" || name == "operator--"; +} + +bool AbstractMetaFunction::isBitwiseOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == "operator<<" || name == "operator<<=" + || name == "operator>>" || name == "operator>>=" + || name == "operator&" || name == "operator&=" + || name == "operator|" || name == "operator|=" + || name == "operator^" || name == "operator^=" + || name == "operator~"; +} + +bool AbstractMetaFunction::isComparisonOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == "operator<" || name == "operator<=" + || name == "operator>" || name == "operator>=" + || name == "operator==" || name == "operator!="; +} + +bool AbstractMetaFunction::isLogicalOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == "operator!" + || name == "operator&&" + || name == "operator||"; +} + +bool AbstractMetaFunction::isSubscriptOperator() const +{ + if (!isOperatorOverload()) + return false; + + return originalName() == "operator[]"; +} + +bool AbstractMetaFunction::isAssignmentOperator() const +{ + if (!isOperatorOverload()) + return false; + + return originalName() == "operator="; +} + +bool AbstractMetaFunction::isOtherOperator() const +{ + if (!isOperatorOverload()) + return false; + + return !isArithmeticOperator() + && !isBitwiseOperator() + && !isComparisonOperator() + && !isLogicalOperator() + && !isConversionOperator() + && !isSubscriptOperator() + && !isAssignmentOperator(); +} + +int AbstractMetaFunction::arityOfOperator() const +{ + if (!isOperatorOverload()) + return -1; + + int arity = m_arguments.size(); + + // Operator overloads that are class members + // implicitly includes the instance and have + // one parameter less than their arity, + // so we increment it. + if (ownerClass() && arity < 2) + arity++; + + return arity; +} + +bool AbstractMetaFunction::isInplaceOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == "operator+=" || name == "operator&=" + || name == "operator-=" || name == "operator|=" + || name == "operator*=" || name == "operator^=" + || name == "operator/=" || name == "operator<<=" + || name == "operator%=" || name == "operator>>="; +} + +bool AbstractMetaFunction::isVirtual() const +{ + return !isFinal() && !isSignal() && !isStatic() && !isFinalInCpp(); +} + +bool AbstractMetaFunction::isCopyConstructor() const +{ + if (!ownerClass() || !isConstructor() || arguments().count() != 1) + return false; + + const AbstractMetaType* type = arguments().first()->type(); + return type->typeEntry() == ownerClass()->typeEntry() && + type->isConstant() && type->isReference(); +} + +QString AbstractMetaFunction::modifiedName() const +{ + if (m_cachedModifiedName.isEmpty()) { + FunctionModificationList mods = modifications(implementingClass()); + foreach (FunctionModification mod, mods) { + if (mod.isRenameModifier()) { + m_cachedModifiedName = mod.renamedToName; + break; + } + } + if (m_cachedModifiedName.isEmpty()) + m_cachedModifiedName = name(); + } + return m_cachedModifiedName; +} + +QString AbstractMetaFunction::targetLangSignature(bool minimal) const +{ + QString s; + + // Attributes... + if (!minimal) { + // Return type + if (type()) + s += type()->name() + " "; + else + s += "void "; + } + + s += modifiedName(); + s += "("; + + int j = 0; + for (int i = 0; i < m_arguments.size(); ++i) { + if (argumentRemoved(i + 1)) + continue; + if (j) { + s += ","; + if (!minimal) + s += QLatin1Char(' '); + } + s += m_arguments.at(i)->type()->name(); + + if (!minimal) { + s += " "; + s += m_arguments.at(i)->argumentName(); + } + ++j; + } + + s += ")"; + + return s; +} + + +bool function_sorter(AbstractMetaFunction *a, AbstractMetaFunction *b) +{ + return a->signature() < b->signature(); +} + +/******************************************************************************* + * AbstractMetaClass + */ +AbstractMetaClass::~AbstractMetaClass() +{ + qDeleteAll(m_functions); + qDeleteAll(m_fields); +} + +/******************************************************************************* + * Returns true if this class is a subclass of the given class + */ +bool AbstractMetaClass::inheritsFrom(const AbstractMetaClass *cls) const +{ + Q_ASSERT(cls); + + const AbstractMetaClass *clazz = this; + while (clazz) { + if (clazz == cls) + return true; + + clazz = clazz->baseClass(); + } + + return false; +} + +/******************************************************************************* + * Constructs an interface based on the functions and enums in this + * class and returns it... + */ +AbstractMetaClass *AbstractMetaClass::extractInterface() +{ + Q_ASSERT(typeEntry()->designatedInterface()); + + if (!m_extractedInterface) { + AbstractMetaClass *iface = new AbstractMetaClass; + iface->setAttributes(attributes()); + iface->setBaseClass(0); + iface->setPrimaryInterfaceImplementor(this); + + iface->setTypeEntry(typeEntry()->designatedInterface()); + + foreach (AbstractMetaFunction *function, functions()) { + if (!function->isConstructor()) + iface->addFunction(function->copy()); + } + +// iface->setEnums(enums()); +// setEnums(AbstractMetaEnumList()); + + foreach (const AbstractMetaField *field, fields()) { + if (field->isPublic()) { + AbstractMetaField *new_field = field->copy(); + new_field->setEnclosingClass(iface); + iface->addField(new_field); + } + } + + m_extractedInterface = iface; + addInterface(iface); + } + + return m_extractedInterface; +} + +/******************************************************************************* + * Returns a list of all the functions with a given name + */ +AbstractMetaFunctionList AbstractMetaClass::queryFunctionsByName(const QString &name) const +{ + AbstractMetaFunctionList returned; + AbstractMetaFunctionList functions = this->functions(); + foreach (AbstractMetaFunction *function, functions) { + if (function->name() == name) + returned.append(function); + } + + return returned; +} + +/******************************************************************************* + * Returns all reference count modifications for any function in the class + */ +QList AbstractMetaClass::referenceCounts() const +{ + QList returned; + + AbstractMetaFunctionList functions = this->functions(); + foreach (AbstractMetaFunction *function, functions) + returned += function->referenceCounts(this); + + return returned; +} + +/******************************************************************************* + * Returns a list of all the functions retrieved during parsing which should + * be added to the API. + */ +AbstractMetaFunctionList AbstractMetaClass::functionsInTargetLang() const +{ + int default_flags = NormalFunctions | Visible | NotRemovedFromTargetLang; + + // Interfaces don't implement functions + default_flags |= isInterface() ? 0 : ClassImplements; + + // Only public functions in final classes + // default_flags |= isFinal() ? WasPublic : 0; + int public_flags = isFinal() ? WasPublic : 0; + + // Constructors + AbstractMetaFunctionList returned = queryFunctions(Constructors | default_flags | public_flags); + + // Final functions + returned += queryFunctions(FinalInTargetLangFunctions | NonStaticFunctions | default_flags | public_flags); + + // Virtual functions + returned += queryFunctions(VirtualInTargetLangFunctions | NonStaticFunctions | default_flags | public_flags); + + // Static functions + returned += queryFunctions(StaticFunctions | default_flags | public_flags); + + // Empty, private functions, since they aren't caught by the other ones + returned += queryFunctions(Empty | Invisible); + + return returned; +} + +AbstractMetaFunctionList AbstractMetaClass::virtualFunctions() const +{ + AbstractMetaFunctionList list = functionsInShellClass(); + + AbstractMetaFunctionList returned; + foreach (AbstractMetaFunction *f, list) { + if (!f->isFinalInCpp() || f->isVirtualSlot()) + returned += f; + } + + return returned; +} + +AbstractMetaFunctionList AbstractMetaClass::nonVirtualShellFunctions() const +{ + AbstractMetaFunctionList list = functionsInShellClass(); + AbstractMetaFunctionList returned; + foreach (AbstractMetaFunction *f, list) { + if (f->isFinalInCpp() && !f->isVirtualSlot()) + returned += f; + } + + return returned; +} + +AbstractMetaFunctionList AbstractMetaClass::implicitConversions() const +{ + AbstractMetaFunctionList list = queryFunctions(Constructors); + AbstractMetaFunctionList returned; + foreach (AbstractMetaFunction *f, list) { + if (f->arguments().size() == 1 && !f->isExplicit() && !f->isCopyConstructor() && hasCloneOperator()) + returned += f; + } + return returned; +} + +AbstractMetaFunctionList AbstractMetaClass::operatorOverloads(uint query) const +{ + AbstractMetaFunctionList list = queryFunctions(OperatorOverloads); + AbstractMetaFunctionList returned; + foreach (AbstractMetaFunction *f, list) { + if (((query & ArithmeticOp) && f->isArithmeticOperator()) + || ((query & BitwiseOp) && f->isBitwiseOperator()) + || ((query & ComparisonOp) && f->isComparisonOperator()) + || ((query & LogicalOp) && f->isLogicalOperator()) + || ((query & SubscriptionOp) && f->isSubscriptOperator()) + || ((query & AssignmentOp) && f->isAssignmentOperator()) + || ((query & ConversionOp) && f->isConversionOperator()) + || ((query & OtherOp) && f->isOtherOperator())) + returned += f; + } + + return returned; +} + +bool AbstractMetaClass::hasOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isOperatorOverload()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasArithmeticOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isArithmeticOperator()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasBitwiseOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isBitwiseOperator()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasComparisonOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isComparisonOperator()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasLogicalOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isLogicalOperator()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasSubscriptOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isSubscriptOperator()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasAssignmentOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isAssignmentOperator()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasConversionOperatorOverload() const +{ + foreach (const AbstractMetaFunction *f, m_functions) { + if (f->isConversionOperator()) + return true; + } + return false; +} + +/******************************************************************************* + * Returns a list of all functions that should be declared and implemented in + * the shell class which is generated as a wrapper on top of the actual C++ class + */ +AbstractMetaFunctionList AbstractMetaClass::functionsInShellClass() const +{ + // Only functions and only protected and public functions + int default_flags = NormalFunctions | Visible | WasVisible | NotRemovedFromShell; + + // All virtual functions + AbstractMetaFunctionList returned = queryFunctions(VirtualFunctions | default_flags); + + // All functions explicitly set to be implemented by the shell class + // (mainly superclass functions that are hidden by other declarations) + returned += queryFunctions(ForcedShellFunctions | default_flags); + + // All functions explicitly set to be virtual slots + returned += queryFunctions(VirtualSlots | default_flags); + + return returned; +} + +/******************************************************************************* + * Returns a list of all functions that require a public override function to + * be generated in the shell class. This includes all functions that were originally + * protected in the superclass. + */ +AbstractMetaFunctionList AbstractMetaClass::publicOverrideFunctions() const +{ + return queryFunctions(NormalFunctions | WasProtected | FinalInCppFunctions | NotRemovedFromTargetLang) + + queryFunctions(Signals | WasProtected | FinalInCppFunctions | NotRemovedFromTargetLang); +} + +AbstractMetaFunctionList AbstractMetaClass::virtualOverrideFunctions() const +{ + return queryFunctions(NormalFunctions | NonEmptyFunctions | Visible | VirtualInCppFunctions | NotRemovedFromShell) + + queryFunctions(Signals | NonEmptyFunctions | Visible | VirtualInCppFunctions | NotRemovedFromShell); +} + +void AbstractMetaClass::sortFunctions() +{ + qSort(m_functions.begin(), m_functions.end(), function_sorter); +} + +void AbstractMetaClass::setFunctions(const AbstractMetaFunctionList &functions) +{ + m_functions = functions; + + // Functions must be sorted by name before next loop + sortFunctions(); + + QString currentName; + bool hasVirtuals = false; + AbstractMetaFunctionList finalFunctions; + foreach (AbstractMetaFunction *f, m_functions) { + f->setOwnerClass(this); + + m_hasVirtualSlots |= f->isVirtualSlot(); + m_hasVirtuals |= !f->isFinal() || f->isVirtualSlot(); + m_isPolymorphic |= m_hasVirtuals; + m_hasNonpublic |= !f->isPublic(); + + // If we have non-virtual overloads of a virtual function, we have to implement + // all the overloads in the shell class to override the hiding rule + if (currentName == f->name()) { + hasVirtuals = hasVirtuals || !f->isFinal(); + if (f->isFinal()) + finalFunctions += f; + } else { + if (hasVirtuals && finalFunctions.size() > 0) { + foreach (AbstractMetaFunction *final_function, finalFunctions) { + *final_function += AbstractMetaAttributes::ForceShellImplementation; + + QString warn = QString("hiding of function '%1' in class '%2'") + .arg(final_function->name()).arg(name()); + ReportHandler::warning(warn); + } + } + + hasVirtuals = !f->isFinal(); + finalFunctions.clear(); + if (f->isFinal()) + finalFunctions += f; + currentName = f->name(); + } + } +} + +bool AbstractMetaClass::hasFieldAccessors() const +{ + foreach (const AbstractMetaField *field, fields()) { + if (field->getter() || field->setter()) + return true; + } + + return false; +} + +bool AbstractMetaClass::hasDefaultToStringFunction() const +{ + foreach (AbstractMetaFunction *f, queryFunctionsByName("toString")) { + if (!f->actualMinimumArgumentCount()) + return true; + } + return false; +} + +void AbstractMetaClass::addFunction(AbstractMetaFunction *function) +{ + function->setOwnerClass(this); + + if (!function->isDestructor()) + m_functions << function; + + m_hasVirtualSlots |= function->isVirtualSlot(); + m_hasVirtuals |= !function->isFinal() || function->isVirtualSlot(); + m_isPolymorphic |= m_hasVirtuals; + m_hasNonpublic |= !function->isPublic(); +} + +bool AbstractMetaClass::hasSignal(const AbstractMetaFunction *other) const +{ + if (!other->isSignal()) + return false; + + foreach (const AbstractMetaFunction *f, functions()) { + if (f->isSignal() && f->compareTo(other) & AbstractMetaFunction::EqualName) + return other->modifiedName() == f->modifiedName(); + } + + return false; +} + + +QString AbstractMetaClass::name() const +{ + return m_typeEntry->targetLangName(); +// return QString(m_typeEntry->targetLangName()).replace("::", "_"); +} + +void AbstractMetaClass::setBaseClass(AbstractMetaClass *baseClass) +{ + m_baseClass = baseClass; + if (baseClass) + m_isPolymorphic |= baseClass->isPolymorphic(); +} + +bool AbstractMetaClass::hasFunction(const QString &str) const +{ + foreach (const AbstractMetaFunction *f, functions()) + if (f->name() == str) + return true; + return false; +} + +/* Returns true if this class has one or more functions that are + protected. If a class has protected members we need to generate a + shell class with public accessors to the protected functions, so + they can be called from the native functions. +*/ +bool AbstractMetaClass::hasProtectedFunctions() const +{ + foreach (AbstractMetaFunction *func, m_functions) { + if (func->isProtected()) + return true; + } + return false; +} + +bool AbstractMetaClass::generateShellClass() const +{ + return m_forceShellClass || + (!isFinal() + && (hasVirtualFunctions() + || hasProtectedFunctions() + || hasFieldAccessors())); +} + +QPropertySpec *AbstractMetaClass::propertySpecForRead(const QString &name) const +{ + for (int i = 0; i < m_propertySpecs.size(); ++i) + if (name == m_propertySpecs.at(i)->read()) + return m_propertySpecs.at(i); + return 0; +} + +QPropertySpec *AbstractMetaClass::propertySpecForWrite(const QString &name) const +{ + for (int i = 0; i < m_propertySpecs.size(); ++i) + if (name == m_propertySpecs.at(i)->write()) + return m_propertySpecs.at(i); + return 0; +} + +QPropertySpec *AbstractMetaClass::propertySpecForReset(const QString &name) const +{ + for (int i = 0; i < m_propertySpecs.size(); ++i) { + if (name == m_propertySpecs.at(i)->reset()) + return m_propertySpecs.at(i); + } + return 0; +} + + + +static bool functions_contains(const AbstractMetaFunctionList &l, const AbstractMetaFunction *func) +{ + foreach (const AbstractMetaFunction *f, l) { + if ((f->compareTo(func) & AbstractMetaFunction::PrettySimilar) == AbstractMetaFunction::PrettySimilar) + return true; + } + return false; +} + +AbstractMetaField::AbstractMetaField() : m_getter(0), m_setter(0), m_class(0) +{ +} + +AbstractMetaField::~AbstractMetaField() +{ + delete m_setter; + delete m_getter; +} +ushort painters; // refcount +AbstractMetaField *AbstractMetaField::copy() const +{ + AbstractMetaField *returned = new AbstractMetaField; + returned->setEnclosingClass(0); + returned->setAttributes(attributes()); + returned->setName(name()); + returned->setType(type()->copy()); + returned->setOriginalAttributes(originalAttributes()); + + return returned; +} + +static QString upCaseFirst(const QString &str) +{ + Q_ASSERT(!str.isEmpty()); + QString s = str; + s[0] = s.at(0).toUpper(); + return s; +} + +static AbstractMetaFunction *createXetter(const AbstractMetaField *g, const QString &name, uint type) +{ + AbstractMetaFunction *f = new AbstractMetaFunction; + + + + f->setName(name); + f->setOriginalName(name); + f->setOwnerClass(g->enclosingClass()); + f->setImplementingClass(g->enclosingClass()); + f->setDeclaringClass(g->enclosingClass()); + + uint attr = AbstractMetaAttributes::Native + | AbstractMetaAttributes::Final + | type; + if (g->isStatic()) + attr |= AbstractMetaAttributes::Static; + if (g->isPublic()) + attr |= AbstractMetaAttributes::Public; + else if (g->isProtected()) + attr |= AbstractMetaAttributes::Protected; + else + attr |= AbstractMetaAttributes::Private; + f->setAttributes(attr); + f->setOriginalAttributes(attr); + + FieldModificationList mods = g->modifications(); + foreach (FieldModification mod, mods) { + if (mod.isRenameModifier()) + f->setName(mod.renamedTo()); + if (mod.isAccessModifier()) { + if (mod.isPrivate()) + f->setVisibility(AbstractMetaAttributes::Private); + else if (mod.isProtected()) + f->setVisibility(AbstractMetaAttributes::Protected); + else if (mod.isPublic()) + f->setVisibility(AbstractMetaAttributes::Public); + else if (mod.isFriendly()) + f->setVisibility(AbstractMetaAttributes::Friendly); + } + } + return f; +} + +FieldModificationList AbstractMetaField::modifications() const +{ + FieldModificationList mods = enclosingClass()->typeEntry()->fieldModifications(); + FieldModificationList returned; + + foreach (FieldModification mod, mods) { + if (mod.name == name()) + returned += mod; + } + + return returned; +} + +const AbstractMetaFunction *AbstractMetaField::setter() const +{ + if (!m_setter) { + m_setter = createXetter(this, + "set" + upCaseFirst(name()), + AbstractMetaAttributes::SetterFunction); + AbstractMetaArgumentList arguments; + AbstractMetaArgument *argument = new AbstractMetaArgument; + argument->setType(type()->copy()); + argument->setName(name()); + arguments.append(argument); + m_setter->setArguments(arguments); + } + return m_setter; +} + +const AbstractMetaFunction *AbstractMetaField::getter() const +{ + if (!m_getter) { + m_getter = createXetter(this, + name(), + AbstractMetaAttributes::GetterFunction); + m_getter->setType(type()); + } + + return m_getter; +} + + +bool AbstractMetaClass::hasConstructors() const +{ + return queryFunctions(Constructors).size(); +} + +void AbstractMetaClass::addDefaultConstructor() +{ + AbstractMetaFunction *f = new AbstractMetaFunction; + f->setName(name()); + f->setOwnerClass(this); + f->setFunctionType(AbstractMetaFunction::ConstructorFunction); + f->setArguments(AbstractMetaArgumentList()); + f->setDeclaringClass(this); + + uint attr = AbstractMetaAttributes::Native; + attr |= AbstractMetaAttributes::Public; + f->setAttributes(attr); + f->setImplementingClass(this); + f->setOriginalAttributes(f->attributes()); + + addFunction(f); +} + +bool AbstractMetaClass::hasFunction(const AbstractMetaFunction *f) const +{ + return functions_contains(m_functions, f); +} + +/* Goes through the list of functions and returns a list of all + functions matching all of the criteria in \a query. + */ + +AbstractMetaFunctionList AbstractMetaClass::queryFunctions(uint query) const +{ + AbstractMetaFunctionList functions; + + foreach (AbstractMetaFunction *f, m_functions) { + + if ((query & VirtualSlots) && !f->isVirtualSlot()) + continue; + + if ((query & NotRemovedFromTargetLang) && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) + continue; + + if ((query & NotRemovedFromTargetLang) && !f->isFinal() && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) + continue; + + if ((query & NotRemovedFromShell) && f->isRemovedFrom(f->implementingClass(), TypeSystem::ShellCode)) + continue; + + if ((query & NotRemovedFromShell) && !f->isFinal() && f->isRemovedFrom(f->declaringClass(), TypeSystem::ShellCode)) + continue; + + if ((query & Visible) && f->isPrivate()) + continue; + + if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) + continue; + + if ((query & Invisible) && !f->isPrivate()) + continue; + + if ((query & Empty) && !f->isEmptyFunction()) + continue; + + if ((query & WasPublic) && !f->wasPublic()) + continue; + + if ((query & WasVisible) && f->wasPrivate()) + continue; + + if ((query & WasProtected) && !f->wasProtected()) + continue; + + if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) + continue; + + if ((query & Inconsistent) && (f->isFinalInTargetLang() || !f->isFinalInCpp() || f->isStatic())) + continue; + + if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) + continue; + + if ((query & FinalInCppFunctions) && !f->isFinalInCpp()) + continue; + + if ((query & VirtualInCppFunctions) && f->isFinalInCpp()) + continue; + + if ((query & Signals) && (!f->isSignal())) + continue; + + if ((query & ForcedShellFunctions) && + (!f->isForcedShellImplementation() || !f->isFinal())) { + continue; + } + + if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) + continue; + + if (!(query & Constructors) && f->isConstructor()) + continue; + + // Destructors are never included in the functions of a class currently + /* + if ((query & Destructors) && (!f->isDestructor() + || f->ownerClass() != f->implementingClass()) + || f->isDestructor() && (query & Destructors) == 0) { + continue; + }*/ + + if ((query & VirtualFunctions) && (f->isFinal() || f->isSignal() || f->isStatic())) + continue; + + if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) + continue; + + if ((query & NonStaticFunctions) && (f->isStatic())) + continue; + + if ((query & NonEmptyFunctions) && (f->isEmptyFunction())) + continue; + + if ((query & NormalFunctions) && (f->isSignal())) + continue; + + if ((query & AbstractFunctions) && !f->isAbstract()) + continue; + + if ((query & OperatorOverloads) && !f->isOperatorOverload()) + continue; + + functions << f; + } + +// qDebug() << "queried" << m_typeEntry->qualifiedCppName() << "got" << functions.size() << "out of" << m_functions.size(); + + return functions; +} + + +bool AbstractMetaClass::hasInconsistentFunctions() const +{ + return cppInconsistentFunctions().size() > 0; +} + +bool AbstractMetaClass::hasSignals() const +{ + return cppSignalFunctions().size() > 0; +} + + +/** + * Adds the specified interface to this class by adding all the + * functions in the interface to this class. + */ +void AbstractMetaClass::addInterface(AbstractMetaClass *interface) +{ + Q_ASSERT(!m_interfaces.contains(interface)); + m_interfaces << interface; + + m_isPolymorphic |= interface->isPolymorphic(); + + if (m_extractedInterface && m_extractedInterface != interface) + m_extractedInterface->addInterface(interface); + +#if 0 + foreach (AbstractMetaFunction *function, interface->functions()) + if (!hasFunction(function) && !function->isConstructor()) { + AbstractMetaFunction *cpy = function->copy(); + cpy->setImplementingClass(this); + + // Setup that this function is an interface class. + cpy->setInterfaceClass(interface); + *cpy += AbstractMetaAttributes::InterfaceFunction; + + // Copy the modifications in interface into the implementing classes. + FunctionModificationList mods = function->modifications(interface); + foreach (const FunctionModification &mod, mods) + m_typeEntry->addFunctionModification(mod); + + // It should be mostly safe to assume that when we implement an interface + // we don't "pass on" pure virtual functions to our sublcasses... +// *cpy -= AbstractMetaAttributes::Abstract; + + addFunction(cpy); + } +#endif + +} + + +void AbstractMetaClass::setInterfaces(const AbstractMetaClassList &interfaces) +{ + m_interfaces = interfaces; + foreach (const AbstractMetaClass* interface, interfaces) { + if (interface) + m_isPolymorphic |= interface->isPolymorphic(); + } +} + + +AbstractMetaEnum *AbstractMetaClass::findEnum(const QString &enumName) +{ + foreach (AbstractMetaEnum *e, m_enums) { + if (e->name() == enumName) + return e; + } + + if (typeEntry()->designatedInterface()) + return extractInterface()->findEnum(enumName); + + return 0; +} + + + + +/*! Recursivly searches for the enum value named \a enumValueName in + this class and its superclasses and interfaces. Values belonging to + \a meta_enum are excluded from the search. +*/ +AbstractMetaEnumValue *AbstractMetaClass::findEnumValue(const QString &enumValueName, AbstractMetaEnum *meta_enum) +{ + foreach (AbstractMetaEnum *e, m_enums) { + if (e == meta_enum) + continue; + foreach (AbstractMetaEnumValue *v, e->values()) { + if (v->name() == enumValueName) + return v; + } + } + + if (typeEntry()->designatedInterface()) + return extractInterface()->findEnumValue(enumValueName, meta_enum); + + if (baseClass()) + return baseClass()->findEnumValue(enumValueName, meta_enum); + + return 0; +} + + +/*! + * Searches through all of this class' enums for a value matching the + * name \a enumValueName. The name is excluding the class/namespace + * prefix. The function recursivly searches interfaces and baseclasses + * of this class. + */ +AbstractMetaEnum *AbstractMetaClass::findEnumForValue(const QString &enumValueName) +{ + foreach (AbstractMetaEnum *e, m_enums) { + foreach (AbstractMetaEnumValue *v, e->values()) { + if (v->name() == enumValueName) + return e; + } + } + + if (typeEntry()->designatedInterface()) + return extractInterface()->findEnumForValue(enumValueName); + + if (baseClass()) + return baseClass()->findEnumForValue(enumValueName); + + return 0; +} + + +static void addExtraIncludeForType(AbstractMetaClass *metaClass, const AbstractMetaType *type) +{ + if (!type) + return; + + Q_ASSERT(metaClass); + const TypeEntry *entry = (type ? type->typeEntry() : 0); + if (entry && entry->isComplex()) { + const ComplexTypeEntry *centry = static_cast(entry); + ComplexTypeEntry *class_entry = metaClass->typeEntry(); + if (class_entry && centry->include().isValid()) + class_entry->addExtraInclude(centry->include()); + } + + if (type->hasInstantiations()) { + QList instantiations = type->instantiations(); + foreach (AbstractMetaType *instantiation, instantiations) + addExtraIncludeForType(metaClass, instantiation); + } +} + +static void addExtraIncludesForFunction(AbstractMetaClass *metaClass, const AbstractMetaFunction *meta_function) +{ + Q_ASSERT(metaClass); + Q_ASSERT(meta_function); + addExtraIncludeForType(metaClass, meta_function->type()); + + AbstractMetaArgumentList arguments = meta_function->arguments(); + foreach (AbstractMetaArgument *argument, arguments) + addExtraIncludeForType(metaClass, argument->type()); +} + +void AbstractMetaClass::fixFunctions() +{ + if (m_functionsFixed) + return; + else + m_functionsFixed = true; + + AbstractMetaClass *superClass = baseClass(); + AbstractMetaFunctionList funcs = functions(); + +// printf("fix functions for %s\n", qPrintable(name())); + + if (superClass) + superClass->fixFunctions(); + int iface_idx = 0; + while (superClass || iface_idx < interfaces().size()) { +// printf(" - base: %s\n", qPrintable(super_class->name())); + + // Since we always traverse the complete hierarchy we are only + // interrested in what each super class implements, not what + // we may have propagated from their base classes again. + AbstractMetaFunctionList superFuncs; + if (superClass) { + // Super classes can never be final + if (superClass->isFinalInTargetLang()) { + ReportHandler::warning("Final class '" + superClass->name() + "' set to non-final, as it is extended by other classes"); + *superClass -= AbstractMetaAttributes::FinalInTargetLang; + } + superFuncs = superClass->queryFunctions(AbstractMetaClass::ClassImplements); + AbstractMetaFunctionList virtuals = superClass->queryFunctions(AbstractMetaClass::VirtualInCppFunctions); + superFuncs += virtuals; + } else { + superFuncs = interfaces().at(iface_idx)->queryFunctions(AbstractMetaClass::NormalFunctions); + AbstractMetaFunctionList virtuals = interfaces().at(iface_idx)->queryFunctions(AbstractMetaClass::VirtualInCppFunctions); + superFuncs += virtuals; + } + + QSet funcsToAdd; + for (int sfi = 0; sfi < superFuncs.size(); ++sfi) { + AbstractMetaFunction *sf = superFuncs.at(sfi); + + if (sf->isRemovedFromAllLanguages(sf->implementingClass())) + continue; + + // we generally don't care about private functions, but we have to get the ones that are + // virtual in case they override abstract functions. + bool add = (sf->isNormal() || sf->isSignal() || sf->isEmptyFunction()); + for (int fi = 0; fi < funcs.size(); ++fi) { + AbstractMetaFunction *f = funcs.at(fi); + if (f->isRemovedFromAllLanguages(f->implementingClass())) + continue; + + uint cmp = f->compareTo(sf); + + if (cmp & AbstractMetaFunction::EqualModifiedName) { +// printf(" - %s::%s similar to %s::%s %x vs %x\n", +// qPrintable(sf->implementingClass()->typeEntry()->qualifiedCppName()), +// qPrintable(sf->name()), +// qPrintable(f->implementingClass()->typeEntry()->qualifiedCppName()), +// qPrintable(f->name()), +// sf->attributes(), +// f->attributes()); + + add = false; + if (cmp & AbstractMetaFunction::EqualArguments) { + +// if (!(cmp & AbstractMetaFunction::EqualReturnType)) { +// ReportHandler::warning(QString("%1::%2 and %3::%4 differ in retur type") +// .arg(sf->implementingClass()->name()) +// .arg(sf->name()) +// .arg(f->implementingClass()->name()) +// .arg(f->name())); +// } + + // Same function, propegate virtual... + if (!(cmp & AbstractMetaFunction::EqualAttributes)) { + if (!f->isEmptyFunction()) { + if (!sf->isFinalInCpp() && f->isFinalInCpp()) { + *f -= AbstractMetaAttributes::FinalInCpp; + // printf(" --- inherit virtual\n"); + } + if (!sf->isFinalInTargetLang() && f->isFinalInTargetLang()) { + *f -= AbstractMetaAttributes::FinalInTargetLang; + // printf(" --- inherit virtual\n"); + } +#if 0 + if (!f->isFinalInTargetLang() && f->isPrivate()) { + f->setFunctionType(AbstractMetaFunction::EmptyFunction); + f->setVisibility(AbstractMetaAttributes::Protected); + *f += AbstractMetaAttributes::FinalInTargetLang; + ReportHandler::warning(QString("private virtual function '%1' in '%2'") + .arg(f->signature()) + .arg(f->implementingClass()->name())); + } +#endif + } + } + + if (f->visibility() != sf->visibility()) { + QString warn = QString("visibility of function '%1' modified in class '%2'") + .arg(f->name()).arg(name()); + ReportHandler::warning(warn); +#if 0 + // If new visibility is private, we can't + // do anything. If it isn't, then we + // prefer the parent class's visibility + // setting for the function. + if (!f->isPrivate() && !sf->isPrivate()) + f->setVisibility(sf->visibility()); +#endif + // Private overrides of abstract functions have to go into the class or + // the subclasses will not compile as non-abstract classes. + // But they don't need to be implemented, since they can never be called. + if (f->isPrivate() && sf->isAbstract()) { + f->setFunctionType(AbstractMetaFunction::EmptyFunction); +// f->setVisibility(sf->visibility()); + *f += AbstractMetaAttributes::FinalInTargetLang; + *f += AbstractMetaAttributes::FinalInCpp; + } + } + + // Set the class which first declares this function, afawk + f->setDeclaringClass(sf->declaringClass()); + + if (sf->isFinalInTargetLang() && !sf->isPrivate() && !f->isPrivate() && !sf->isStatic() && !f->isStatic()) { + // Shadowed funcion, need to make base class + // function non-virtual + if (f->implementingClass() != sf->implementingClass() && f->implementingClass()->inheritsFrom(sf->implementingClass())) { + + // Check whether the superclass method has been redefined to non-final + + bool hasNonFinalModifier = false; + bool isBaseImplPrivate = false; + FunctionModificationList mods = sf->modifications(sf->implementingClass()); + foreach (FunctionModification mod, mods) { + if (mod.isNonFinal()) { + hasNonFinalModifier = true; + break; + } else if (mod.isPrivate()) { + isBaseImplPrivate = true; + break; + } + } + + if (!hasNonFinalModifier && !isBaseImplPrivate) { + ReportHandler::warning(QString::fromLatin1("Shadowing: %1::%2 and %3::%4") + .arg(sf->implementingClass()->name()) + .arg(sf->signature()) + .arg(f->implementingClass()->name()) + .arg(f->signature())); + } + } + } + + } + + if (cmp & AbstractMetaFunction::EqualDefaultValueOverload) { + AbstractMetaArgumentList arguments; + if (f->arguments().size() < sf->arguments().size()) + arguments = sf->arguments(); + else + arguments = f->arguments(); + //TODO: fix this + //for (int i=0; isetDefaultValueExpression("<#>" + QString()); + } + + + // Otherwise we have function shadowing and we can + // skip the thing... + } else if (cmp & AbstractMetaFunction::EqualName && !sf->isSignal()) { + // In the case of function shadowing where the function name has been altered to + // avoid conflict, we don't copy in the original. + add = false; + } + } + + if (add) + funcsToAdd << sf; + } + + foreach (AbstractMetaFunction *f, funcsToAdd) + funcs << f->copy(); + + if (superClass) + superClass = superClass->baseClass(); + else + iface_idx++; + } + + bool hasPrivateConstructors = false; + bool hasPublicConstructors = false; + foreach (AbstractMetaFunction *func, funcs) { + FunctionModificationList mods = func->modifications(this); + foreach (const FunctionModification &mod, mods) { + if (mod.isRenameModifier()) { +// qDebug() << name() << func->originalName() << func << " from " +// << func->implementingClass()->name() << "renamed to" << mod.renamedTo(); + func->setName(mod.renamedTo()); + } + } + + // Make sure class is abstract if one of the functions is + if (func->isAbstract()) { + (*this) += AbstractMetaAttributes::Abstract; + (*this) -= AbstractMetaAttributes::Final; + } + + if (func->isConstructor()) { + if (func->isPrivate()) + hasPrivateConstructors = true; + else + hasPublicConstructors = true; + } + + + + // Make sure that we include files for all classes that are in use + + if (!func->isRemovedFrom(this, TypeSystem::ShellCode)) + addExtraIncludesForFunction(this, func); + } + + if (hasPrivateConstructors && !hasPublicConstructors) { + (*this) += AbstractMetaAttributes::Abstract; + (*this) -= AbstractMetaAttributes::Final; + } + + foreach (AbstractMetaFunction *f1, funcs) { + foreach (AbstractMetaFunction *f2, funcs) { + if (f1 != f2) { + uint cmp = f1->compareTo(f2); + if ((cmp & AbstractMetaFunction::EqualName) + && !f1->isFinalInCpp() + && f2->isFinalInCpp()) { + *f2 += AbstractMetaAttributes::FinalOverload; +// qDebug() << f2 << f2->implementingClass()->name() << "::" << f2->name() << f2->arguments().size() << " vs " << f1 << f1->implementingClass()->name() << "::" << f1->name() << f1->arguments().size(); +// qDebug() << " " << f2; +// AbstractMetaArgumentList f2Args = f2->arguments(); +// foreach (AbstractMetaArgument *a, f2Args) +// qDebug() << " " << a->type()->name() << a->name(); +// qDebug() << " " << f1; +// AbstractMetaArgumentList f1Args = f1->arguments(); +// foreach (AbstractMetaArgument *a, f1Args) +// qDebug() << " " << a->type()->name() << a->name(); + + } + } + } + } + + setFunctions(funcs); +} + + +QString AbstractMetaType::minimalSignature() const +{ + QString minimalSignature; + if (isConstant()) + minimalSignature += "const "; + minimalSignature += typeEntry()->qualifiedCppName(); + if (hasInstantiations()) { + QList instantiations = this->instantiations(); + minimalSignature += "< "; + for (int i = 0; i < instantiations.size(); ++i) { + if (i > 0) + minimalSignature += ","; + minimalSignature += instantiations.at(i)->minimalSignature(); + } + minimalSignature += " >"; + } + + if (isReference()) + minimalSignature += "&"; + for (int j = 0; j < indirections(); ++j) + minimalSignature += "*"; + + return minimalSignature; +} + +bool AbstractMetaType::hasNativeId() const +{ + return (isQObject() || isValue() || isObject()) && typeEntry()->isNativeIdBased(); +} + + +/******************************************************************************* + * Other stuff... + */ + + +AbstractMetaEnum *AbstractMetaClassList::findEnum(const EnumTypeEntry *entry) const +{ + Q_ASSERT(entry->isEnum()); + + QString qualifiedName = entry->qualifiedCppName(); + int pos = qualifiedName.lastIndexOf("::"); + + QString enumName; + QString className; + + if (pos > 0) { + enumName = qualifiedName.mid(pos + 2); + className = qualifiedName.mid(0, pos); + } else { + enumName = qualifiedName; + className = TypeDatabase::globalNamespaceClassName(entry); + } + + AbstractMetaClass *metaClass = findClass(className); + if (!metaClass) { + ReportHandler::warning(QString("AbstractMeta::findEnum(), unknown class '%1' in '%2'") + .arg(className).arg(entry->qualifiedCppName())); + return 0; + } + + return metaClass->findEnum(enumName); +} + +AbstractMetaEnumValue *AbstractMetaEnumValueList::find(const QString &name) const +{ + for (int i = 0; i < size(); ++i) { + if (name == at(i)->name()) + return at(i); + } + return 0; +} + +AbstractMetaEnumValue *AbstractMetaClassList::findEnumValue(const QString &name) const +{ + QStringList lst = name.split(QLatin1String("::")); + + Q_ASSERT_X(lst.size() == 2, "AbstractMetaClassList::findEnumValue()", "Expected qualified enum"); + + + QString prefixName = lst.at(0); + QString enumName = lst.at(1); + + AbstractMetaClass *cl = findClass(prefixName); + if (cl) + return cl->findEnumValue(enumName, 0); + + ReportHandler::warning(QString("no matching enum '%1'").arg(name)); + return 0; +} + +/*! + * Searches the list after a class that mathces \a name; either as + * C++, Target language base name or complete Target language package.class name. + */ + +AbstractMetaClass *AbstractMetaClassList::findClass(const QString &name) const +{ + if (name.isEmpty()) + return 0; + + foreach (AbstractMetaClass *c, *this) { + if (c->qualifiedCppName() == name) + return c; + } + + foreach (AbstractMetaClass *c, *this) { + if (c->fullName() == name) + return c; + } + + foreach (AbstractMetaClass *c, *this) { + if (c->name() == name) + return c; + } + + return 0; +} diff --git a/abstractmetalang.h b/abstractmetalang.h new file mode 100644 index 000000000..a7b0be1c0 --- /dev/null +++ b/abstractmetalang.h @@ -0,0 +1,1874 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef ABSTRACTMETALANG_H +#define ABSTRACTMETALANG_H + +#include "typesystem.h" + +#include +#include +#include + + +class AbstractMeta; +class AbstractMetaClass; +class AbstractMetaField; +class AbstractMetaFunction; +class AbstractMetaType; +class AbstractMetaVariable; +class AbstractMetaArgument; +class AbstractMetaEnumValue; +class AbstractMetaEnum; +class QPropertySpec; + +class Documentation +{ +public: + enum Format { + Native, + Target + }; + + Documentation() + : m_format(Documentation::Native) {} + + Documentation(const QString& value, Format fmt = Documentation::Native) + : m_data(value), m_format(fmt) {} + + QString value() const + { + return m_data; + } + + void setValue(const QString& value, Format fmt = Documentation::Native) + { + m_data = value; m_format = fmt; + } + + Documentation::Format format() const + { + return m_format; + } + +private: + QString m_data; + Format m_format; + +}; + +typedef QList AbstractMetaFieldList; +typedef QList AbstractMetaArgumentList; +typedef QList AbstractMetaFunctionList; +class AbstractMetaClassList : public QList +{ +public: + AbstractMetaClass *findClass(const QString &name) const; + AbstractMetaEnumValue *findEnumValue(const QString &string) const; + AbstractMetaEnum *findEnum(const EnumTypeEntry *entry) const; + +}; + + + +class AbstractMetaAttributes +{ +public: + AbstractMetaAttributes() : m_attributes(0) {}; + + enum Attribute { + None = 0x00000000, + + Private = 0x00000001, + Protected = 0x00000002, + Public = 0x00000004, + Friendly = 0x00000008, + Visibility = 0x0000000f, + + Native = 0x00000010, + Abstract = 0x00000020, + Static = 0x00000040, + + FinalInTargetLang = 0x00000080, + FinalInCpp = 0x00000100, + ForceShellImplementation = 0x00000200, + + GetterFunction = 0x00000400, + SetterFunction = 0x00000800, + + FinalOverload = 0x00001000, + InterfaceFunction = 0x00002000, + + PropertyReader = 0x00004000, + PropertyWriter = 0x00008000, + PropertyResetter = 0x00010000, + + Fake = 0x00020000, + + Invokable = 0x00040000, + + Final = FinalInTargetLang | FinalInCpp + }; + + uint attributes() const + { + return m_attributes; + } + + void setAttributes(uint attributes) + { + m_attributes = attributes; + } + + uint originalAttributes() const + { + return m_originalAttributes; + } + + void setOriginalAttributes(uint attributes) + { + m_originalAttributes = attributes; + } + + uint visibility() const + { + return m_attributes & Visibility; + } + + void setVisibility(uint visi) + { + m_attributes = (m_attributes & ~Visibility) | visi; + } + + void operator+=(Attribute attribute) + { + m_attributes |= attribute; + } + + void operator-=(Attribute attribute) + { + m_attributes &= ~attribute; + } + + bool isNative() const + { + return m_attributes & Native; + } + + bool isFinal() const + { + return (m_attributes & Final) == Final; + } + + bool isFinalInTargetLang() const + { + return m_attributes & FinalInTargetLang; + } + + bool isFinalInCpp() const + { + return m_attributes & FinalInCpp; + } + + bool isAbstract() const + { + return m_attributes & Abstract; + } + + bool isStatic() const + { + return m_attributes & Static; + } + + bool isForcedShellImplementation() const + { + return m_attributes & ForceShellImplementation; + } + + bool isInterfaceFunction() const + { + return m_attributes & InterfaceFunction; + } + + bool isFinalOverload() const + { + return m_attributes & FinalOverload; + } + + bool isInvokable() const + { + return m_attributes & Invokable; + } + + bool isPropertyReader() const + { + return m_attributes & PropertyReader; + } + + bool isPropertyWriter() const + { + return m_attributes & PropertyWriter; + } + + bool isPropertyResetter() const + { + return m_attributes & PropertyResetter; + } + + bool isPrivate() const + { + return m_attributes & Private; + } + + bool isProtected() const + { + return m_attributes & Protected; + } + + bool isPublic() const + { + return m_attributes & Public; + } + + bool isFriendly() const + { + return m_attributes & Friendly; + } + + bool wasPrivate() const + { + return m_originalAttributes & Private; + } + + bool wasProtected() const + { + return m_originalAttributes & Protected; + } + + bool wasPublic() const + { + return m_originalAttributes & Public; + } + + bool wasFriendly() const + { + return m_originalAttributes & Friendly; + } + + // valid only for global components + // (e.g.: functions, enums, flags) + QString includeFile() const + { + return m_includeFile; + } + + void setIncludeFile(const QString& file) + { + m_includeFile = file; + } + + void setDocumentation(const Documentation& doc) + { + m_doc = doc; + } + + Documentation documentation() const + { + return m_doc; + } + +private: + uint m_attributes; + uint m_originalAttributes; + QString m_includeFile; + Documentation m_doc; +}; + + +class AbstractMetaType +{ +public: + enum TypeUsagePattern { + InvalidPattern, + PrimitivePattern, + FlagsPattern, + EnumPattern, + ValuePattern, + StringPattern, + CharPattern, + ObjectPattern, + QObjectPattern, + NativePointerPattern, + ContainerPattern, + VariantPattern, + JObjectWrapperPattern, + ArrayPattern, + ThreadPattern + }; + + AbstractMetaType() : + m_typeEntry(0), + m_arrayElementCount(0), + m_arrayElementType(0), + m_originalTemplateType(0), + m_pattern(InvalidPattern), + m_constant(false), + m_reference(false), + m_cppInstantiation(true), + m_indirections(0), + m_reserved(0) + {} + + QString package() const + { + return m_typeEntry->targetLangPackage(); + } + QString name() const + { + return m_typeEntry->targetLangName(); + } + QString fullName() const + { + return m_typeEntry->qualifiedTargetLangName(); + } + + void setTypeUsagePattern(TypeUsagePattern pattern) + { + m_pattern = pattern; + } + TypeUsagePattern typeUsagePattern() const + { + return m_pattern; + } + + // true when use pattern is container + bool hasInstantiations() const + { + return !m_instantiations.isEmpty(); + } + void addInstantiation(AbstractMetaType *inst) + { + m_instantiations << inst; + } + void setInstantiations(const QList &insts) + { + m_instantiations = insts; + } + QList instantiations() const + { + return m_instantiations; + } + void setInstantiationInCpp(bool incpp) + { + m_cppInstantiation = incpp; + } + bool hasInstantiationInCpp() const + { + return hasInstantiations() && m_cppInstantiation; + } + + QString minimalSignature() const; + + // true when the type is a QtJambiObject subclass + bool hasNativeId() const; + + // returns true if the typs is used as a non complex primitive, no & or *'s + bool isPrimitive() const + { + return m_pattern == PrimitivePattern; + } + + // returns true if the type is used as an enum + bool isEnum() const + { + return m_pattern == EnumPattern; + } + + // returns true if the type is used as a QObject * + bool isQObject() const + { + return m_pattern == QObjectPattern; + } + + // returns true if the type is used as an object, e.g. Xxx * + bool isObject() const + { + return m_pattern == ObjectPattern; + } + + // returns true if the type is used as an array, e.g. Xxx[42] + bool isArray() const + { + return m_pattern == ArrayPattern; + } + + // returns true if the type is used as a value type (X or const X &) + bool isValue() const + { + return m_pattern == ValuePattern; + } + + // returns true for more complex types... + bool isNativePointer() const + { + return m_pattern == NativePointerPattern; + } + + // returns true if the type was originally a QString or const QString & or equivalent for QLatin1String + bool isTargetLangString() const + { + return m_pattern == StringPattern; + } + + // returns true if the type was originally a QChar or const QChar & + bool isTargetLangChar() const + { + return m_pattern == CharPattern; + } + + // return true if the type was originally a QVariant or const QVariant & + bool isVariant() const + { + return m_pattern == VariantPattern; + } + + // return true if the type was originally a JObjectWrapper or const JObjectWrapper & + bool isJObjectWrapper() const + { + return m_pattern == JObjectWrapperPattern; + } + + // returns true if the type was used as a container + bool isContainer() const + { + return m_pattern == ContainerPattern; + } + + // returns true if the type was used as a flag + bool isFlags() const + { + return m_pattern == FlagsPattern; + } + + // returns true if the type was used as a thread + bool isThread() const + { + return m_pattern == ThreadPattern; + } + + bool isConstant() const + { + return m_constant; + } + void setConstant(bool constant) + { + m_constant = constant; + } + + bool isReference() const + { + return m_reference; + } + void setReference(bool ref) + { + m_reference = ref; + } + + /** + * Says if the type is to be implemented using target language + * equivalent of C++ enums, i.e. not plain ints. + * /return true if the type is to be implemented using target + * language enums + */ + bool isTargetLangEnum() const + { + return isEnum() && !((EnumTypeEntry *) typeEntry())->forceInteger(); + } + bool isIntegerEnum() const + { + return isEnum() && !isTargetLangEnum(); + } + + /** + * Says if the type is to be implemented using target language + * equivalent of Qt's QFlags, i.e. not plain ints. + * /return true if the type is to be implemented using target + * language QFlags + */ + bool isTargetLangFlags() const + { + return isFlags() && !((FlagsTypeEntry *) typeEntry())->forceInteger(); + } + bool isIntegerFlags() const + { + return isFlags() && !isTargetLangFlags(); + } + + int actualIndirections() const + { + return m_indirections + (isReference() ? 1 : 0); + } + int indirections() const + { + return m_indirections; + } + void setIndirections(int indirections) + { + m_indirections = indirections; + } + + void setArrayElementCount(int n) + { + m_arrayElementCount = n; + } + int arrayElementCount() const + { + return m_arrayElementCount; + } + + AbstractMetaType *arrayElementType() const + { + return m_arrayElementType; + } + void setArrayElementType(AbstractMetaType *t) + { + m_arrayElementType = t; + } + + QString cppSignature() const; + + AbstractMetaType *copy() const; + + const TypeEntry *typeEntry() const + { + return m_typeEntry; + } + void setTypeEntry(const TypeEntry *type) + { + m_typeEntry = type; + } + + void setOriginalTypeDescription(const QString &otd) + { + m_originalTypeDescription = otd; + } + QString originalTypeDescription() const + { + return m_originalTypeDescription; + } + + void setOriginalTemplateType(const AbstractMetaType *type) + { + m_originalTemplateType = type; + } + const AbstractMetaType *originalTemplateType() const + { + return m_originalTemplateType; + } + +private: + const TypeEntry *m_typeEntry; + QList m_instantiations; + QString m_package; + QString m_originalTypeDescription; + + int m_arrayElementCount; + AbstractMetaType *m_arrayElementType; + const AbstractMetaType *m_originalTemplateType; + + TypeUsagePattern m_pattern; + uint m_constant : 1; + uint m_reference : 1; + uint m_cppInstantiation : 1; + int m_indirections : 4; + uint m_reserved : 25; // unused +}; + +class AbstractMetaVariable +{ +public: + AbstractMetaVariable() : m_type(0) {} + + AbstractMetaType *type() const + { + return m_type; + } + void setType(AbstractMetaType *type) + { + m_type = type; + } + + QString name() const + { + return m_name; + } + void setName(const QString &name) + { + m_name = name; + } + + void setDocumentation(const Documentation& doc) + { + m_doc = doc; + } + Documentation documentation() const + { + return m_doc; + } + +private: + QString m_name; + AbstractMetaType *m_type; + + Documentation m_doc; +}; + + + +class AbstractMetaArgument : public AbstractMetaVariable +{ +public: + AbstractMetaArgument() : m_argumentIndex(0) {}; + + QString defaultValueExpression() const + { + return m_expression; + } + void setDefaultValueExpression(const QString &expr) + { + m_expression = expr; + } + + QString originalDefaultValueExpression() const + { + return m_originalExpression; + } + void setOriginalDefaultValueExpression(const QString &expr) + { + m_originalExpression = expr; + } + + QString toString() const + { + return type()->name() + " " + AbstractMetaVariable::name() + + (m_expression.isEmpty() ? "" : " = " + m_expression); + } + + int argumentIndex() const + { + return m_argumentIndex; + } + void setArgumentIndex(int argIndex) + { + m_argumentIndex = argIndex; + } + + QString argumentName() const; + QString indexedName() const; + + AbstractMetaArgument *copy() const; + +private: + // Just to force people to call argumentName() And indexedName(); + QString name() const; + + QString m_expression; + QString m_originalExpression; + int m_argumentIndex; +}; + + +class AbstractMetaField : public AbstractMetaVariable, public AbstractMetaAttributes +{ +public: + AbstractMetaField(); + ~AbstractMetaField(); + + const AbstractMetaClass *enclosingClass() const + { + return m_class; + } + void setEnclosingClass(const AbstractMetaClass *cls) + { + m_class = cls; + } + + const AbstractMetaFunction *getter() const; + const AbstractMetaFunction *setter() const; + + FieldModificationList modifications() const; + + using AbstractMetaVariable::setDocumentation; + using AbstractMetaVariable::documentation; + + AbstractMetaField *copy() const; + +private: + mutable AbstractMetaFunction *m_getter; + mutable AbstractMetaFunction *m_setter; + const AbstractMetaClass *m_class; +}; + + +class AbstractMetaFunction : public AbstractMetaAttributes +{ +public: + enum FunctionType { + ConstructorFunction, + DestructorFunction, + NormalFunction, + SignalFunction, + EmptyFunction, + SlotFunction, + GlobalScopeFunction + }; + + enum CompareResult { + EqualName = 0x00000001, + EqualArguments = 0x00000002, + EqualAttributes = 0x00000004, + EqualImplementor = 0x00000008, + EqualReturnType = 0x00000010, + EqualDefaultValueOverload = 0x00000020, + EqualModifiedName = 0x00000040, + + NameLessThan = 0x00001000, + + PrettySimilar = EqualName | EqualArguments, + Equal = 0x0000001f, + NotEqual = 0x00001000 + }; + + AbstractMetaFunction() + : m_functionType(NormalFunction), + m_type(0), + m_class(0), + m_implementingClass(0), + m_declaringClass(0), + m_interfaceClass(0), + m_propertySpec(0), + m_constant(false), + m_invalid(false), + m_reverse(false), + m_explicit(false) + { + } + + ~AbstractMetaFunction(); + + QString name() const + { + return m_name; + } + + void setName(const QString &name) + { + m_name = name; + } + + QString originalName() const + { + return m_originalName.isEmpty() ? name() : m_originalName; + } + + void setOriginalName(const QString &name) + { + m_originalName = name; + } + + void setReverseOperator(bool reverse) + { + m_reverse = reverse; + } + + bool isReverseOperator() const + { + return m_reverse; + } + + void setExplicit(bool isExplicit) + { + m_explicit = isExplicit; + } + /** + * Says if the function (a constructor) was declared as explicit in C++. + * \return true if the function was declared as explicit in C++ + */ + bool isExplicit() const + { + return m_explicit; + } + + static bool isConversionOperator(QString funcName); + bool isConversionOperator() const + { + return isConversionOperator(originalName()); + } + + static bool isOperatorOverload(QString funcName); + bool isOperatorOverload() const + { + return isOperatorOverload(originalName()); + } + + bool isArithmeticOperator() const; + bool isBitwiseOperator() const; + bool isComparisonOperator() const; + bool isLogicalOperator() const; + bool isSubscriptOperator() const; + bool isAssignmentOperator() const; + bool isOtherOperator() const; + + /** + * Informs the arity of the operator or -1 if the function is not + * an operator overload. + * /return the arity of the operator or -1 + */ + int arityOfOperator() const; + bool isUnaryOperator() const { return arityOfOperator() == 1; } + bool isBinaryOperator() const { return arityOfOperator() == 2; } + bool isInplaceOperator() const; + + // TODO: ths function *should* know if it is virtual + // instead of asking to your implementing class. + bool isVirtual() const; + bool isCopyConstructor() const; + bool isThread() const; + bool allowThread() const; + QString modifiedName() const; + + QString minimalSignature() const; + QStringList possibleIntrospectionCompatibleSignatures() const; + + QString marshalledName() const; + + // true if one or more of the arguments are of QtJambiObject subclasses + bool argumentsHaveNativeId() const + { + foreach (const AbstractMetaArgument *arg, m_arguments) { + if (arg->type()->hasNativeId()) + return true; + } + + return false; + } + + bool isModifiedRemoved(int types = TypeSystem::All) const; + + AbstractMetaType *type() const + { + return m_type; + } + void setType(AbstractMetaType *type) + { + m_type = type; + } + + // The class that has this function as a member. + const AbstractMetaClass *ownerClass() const + { + return m_class; + } + void setOwnerClass(const AbstractMetaClass *cls) + { + m_class = cls; + } + + // The first class in a hierarchy that declares the function + const AbstractMetaClass *declaringClass() const + { + return m_declaringClass; + } + void setDeclaringClass(const AbstractMetaClass *cls) + { + m_declaringClass = cls; + } + + // The class that actually implements this function + const AbstractMetaClass *implementingClass() const + { + return m_implementingClass; + } + void setImplementingClass(const AbstractMetaClass *cls) + { + m_implementingClass = cls; + } + + bool needsCallThrough() const; + + AbstractMetaArgumentList arguments() const + { + return m_arguments; + } + void setArguments(const AbstractMetaArgumentList &arguments) + { + m_arguments = arguments; + } + void addArgument(AbstractMetaArgument *argument) + { + m_arguments << argument; + } + int actualMinimumArgumentCount() const; + + void setInvalid(bool on) + { + m_invalid = on; + } + bool isInvalid() const + { + return m_invalid; + } + bool isDeprecated() const; + bool isDestructor() const + { + return functionType() == DestructorFunction; + } + bool isConstructor() const + { + return functionType() == ConstructorFunction; + } + bool isNormal() const + { + return functionType() == NormalFunction || isSlot() || isInGlobalScope(); + } + bool isInGlobalScope() const + { + return functionType() == GlobalScopeFunction; + } + bool isSignal() const + { + return functionType() == SignalFunction; + } + bool isSlot() const + { + return functionType() == SlotFunction; + } + bool isEmptyFunction() const + { + return functionType() == EmptyFunction; + } + FunctionType functionType() const + { + return m_functionType; + } + void setFunctionType(FunctionType type) + { + m_functionType = type; + } + + QStringList introspectionCompatibleSignatures(const QStringList &resolvedArguments = QStringList()) const; + QString signature() const; + QString targetLangSignature(bool minimal = false) const; + bool shouldReturnThisObject() const + { + return QLatin1String("this") == argumentReplaced(0); + } + bool shouldIgnoreReturnValue() const + { + return QLatin1String("void") == argumentReplaced(0); + } + + bool isConstant() const + { + return m_constant; + } + void setConstant(bool constant) + { + m_constant = constant; + } + + QString toString() const + { + return m_name; + } + + uint compareTo(const AbstractMetaFunction *other) const; + + bool operator <(const AbstractMetaFunction &a) const; + + AbstractMetaFunction *copy() const; + + QString replacedDefaultExpression(const AbstractMetaClass *cls, int idx) const; + bool removedDefaultExpression(const AbstractMetaClass *cls, int idx) const; + QString conversionRule(TypeSystem::Language language, int idx) const; + QList referenceCounts(const AbstractMetaClass *cls, int idx = -2) const; + ArgumentOwner argumentOwner(const AbstractMetaClass *cls, int idx) const; + + bool nullPointersDisabled(const AbstractMetaClass *cls = 0, int argument_idx = 0) const; + QString nullPointerDefaultValue(const AbstractMetaClass *cls = 0, int argument_idx = 0) const; + + bool resetObjectAfterUse(int argument_idx) const; + + // Returns whether garbage collection is disabled for the argument in any context + bool disabledGarbageCollection(const AbstractMetaClass *cls, int key) const; + + // Returns the ownership rules for the given argument in the given context + TypeSystem::Ownership ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int idx) const; + + bool isVirtualSlot() const; + + QString typeReplaced(int argument_index) const; + bool isRemovedFromAllLanguages(const AbstractMetaClass *) const; + bool isRemovedFrom(const AbstractMetaClass *, TypeSystem::Language language) const; + bool argumentRemoved(int) const; + + QString argumentReplaced(int key) const; + bool needsSuppressUncheckedWarning() const; + + bool hasModifications(const AbstractMetaClass *implementor) const; + /** + * Verifies if any modification to the function is an inject code. + * \return true if there is inject code modifications to the function. + */ + bool hasInjectedCode() const; + /** + * Verifies if any modification to the function alters/removes its + * arguments types or default values. + * \return true if there is some modification to function signature + */ + bool hasSignatureModifications() const; + FunctionModificationList modifications(const AbstractMetaClass *implementor) const; + + // If this function stems from an interface, this returns the + // interface that declares it. + const AbstractMetaClass *interfaceClass() const + { + return m_interfaceClass; + } + + void setInterfaceClass(const AbstractMetaClass *cl) + { + m_interfaceClass = cl; + } + + void setPropertySpec(QPropertySpec *spec) + { + m_propertySpec = spec; + } + + QPropertySpec *propertySpec() const + { + return m_propertySpec; + } + +private: + QString m_name; + QString m_originalName; + mutable QString m_cachedMinimalSignature; + mutable QString m_cachedModifiedName; + + FunctionType m_functionType; + AbstractMetaType *m_type; + const AbstractMetaClass *m_class; + const AbstractMetaClass *m_implementingClass; + const AbstractMetaClass *m_declaringClass; + const AbstractMetaClass *m_interfaceClass; + QPropertySpec *m_propertySpec; + AbstractMetaArgumentList m_arguments; + uint m_constant : 1; + uint m_invalid : 1; + uint m_reverse : 1; + uint m_explicit : 1; +}; + + +class AbstractMetaEnumValue +{ +public: + AbstractMetaEnumValue() + : m_valueSet(false), m_value(0) + { + } + + int value() const + { + return m_value; + } + + void setValue(int value) + { + m_valueSet = true; + m_value = value; + } + + QString stringValue() const + { + return m_stringValue; + } + + void setStringValue(const QString &v) + { + m_stringValue = v; + } + + QString name() const + { + return m_name; + } + + void setName(const QString &name) + { + m_name = name; + } + + bool isValueSet() const + { + return m_valueSet; + } + + void setDocumentation(const Documentation& doc) + { + m_doc = doc; + } + + Documentation documentation() const + { + return m_doc; + } + +private: + QString m_name; + QString m_stringValue; + + bool m_valueSet; + int m_value; + + Documentation m_doc; +}; + + +class AbstractMetaEnumValueList : public QList +{ +public: + AbstractMetaEnumValue *find(const QString &name) const; +}; + +class AbstractMetaEnum : public AbstractMetaAttributes +{ +public: + AbstractMetaEnum() : m_typeEntry(0), m_class(0), m_hasQenumsDeclaration(false) {} + + AbstractMetaEnumValueList values() const + { + return m_enumValues; + } + + void addEnumValue(AbstractMetaEnumValue *enumValue) + { + m_enumValues << enumValue; + } + + QString name() const + { + return m_typeEntry->targetLangName(); + } + + QString qualifier() const + { + return m_typeEntry->targetLangQualifier(); + } + + QString package() const + { + return m_typeEntry->targetLangPackage(); + } + + QString fullName() const + { + return package() + "." + qualifier() + "." + name(); + } + + // Has the enum been declared inside a Q_ENUMS() macro in its enclosing class? + void setHasQEnumsDeclaration(bool on) + { + m_hasQenumsDeclaration = on; + } + + bool hasQEnumsDeclaration() const + { + return m_hasQenumsDeclaration; + } + + EnumTypeEntry *typeEntry() const + { + return m_typeEntry; + } + + void setTypeEntry(EnumTypeEntry *entry) + { + m_typeEntry = entry; + } + + AbstractMetaClass *enclosingClass() const + { + return m_class; + } + + void setEnclosingClass(AbstractMetaClass *c) + { + m_class = c; + } + +private: + AbstractMetaEnumValueList m_enumValues; + EnumTypeEntry *m_typeEntry; + AbstractMetaClass *m_class; + + uint m_hasQenumsDeclaration : 1; + uint m_reserved : 31; +}; + +typedef QList AbstractMetaEnumList; + +class AbstractMetaClass : public AbstractMetaAttributes +{ +public: + enum FunctionQueryOption { + Constructors = 0x0000001, // Only constructors + //Destructors = 0x0000002, // Only destructors. Not included in class. + VirtualFunctions = 0x0000004, // Only virtual functions (virtual in both TargetLang and C++) + FinalInTargetLangFunctions = 0x0000008, // Only functions that are non-virtual in TargetLang + FinalInCppFunctions = 0x0000010, // Only functions that are non-virtual in C++ + ClassImplements = 0x0000020, // Only functions implemented by the current class + Inconsistent = 0x0000040, // Only inconsistent functions (inconsistent virtualness in TargetLang/C++) + StaticFunctions = 0x0000080, // Only static functions + Signals = 0x0000100, // Only signals + NormalFunctions = 0x0000200, // Only functions that aren't signals + Visible = 0x0000400, // Only public and protected functions + ForcedShellFunctions = 0x0000800, // Only functions that are overridden to be implemented in the shell class + WasPublic = 0x0001000, // Only functions that were originally public + WasProtected = 0x0002000, // Only functions that were originally protected + NonStaticFunctions = 0x0004000, // No static functions + Empty = 0x0008000, // Empty overrides of abstract functions + Invisible = 0x0010000, // Only private functions + VirtualInCppFunctions = 0x0020000, // Only functions that are virtual in C++ + NonEmptyFunctions = 0x0040000, // Only functions with target language API implementations + VirtualInTargetLangFunctions = 0x0080000, // Only functions which are virtual in TargetLang + AbstractFunctions = 0x0100000, // Only abstract functions + WasVisible = 0x0200000, // Only functions that were public or protected in the original code + NotRemovedFromTargetLang = 0x0400000, // Only functions that have not been removed from TargetLang + NotRemovedFromShell = 0x0800000, // Only functions that have not been removed from the shell class + VirtualSlots = 0x1000000, // Only functions that are set as virtual slots in the type system + OperatorOverloads = 0x2000000 // Only functions that are operator overloads + }; + + enum OperatorQueryOption { + ArithmeticOp = 0x01, // Arithmetic: +, -, *, /, %, +=, -=, *=, /=, %=, ++, --, unary+, unary- + BitwiseOp = 0x02, // Bitwise: <<, <<=, >>, >>=, ~, &, &=, |, |=, ^, ^= + ComparisonOp = 0x04, // Comparison: <, <=, >, >=, !=, == + LogicalOp = 0x08, // Logical: !, &&, || + ConversionOp = 0x10, // Conversion: operator [const] TYPE() + SubscriptionOp = 0x20, // Subscription: [] + AssignmentOp = 0x40, // Assignment: = + OtherOp = 0x80, // The remaining operators: call(), etc + AllOperators = ArithmeticOp | BitwiseOp | ComparisonOp + | LogicalOp | ConversionOp | SubscriptionOp + | AssignmentOp | OtherOp + }; + + AbstractMetaClass() + : m_namespace(false), + m_qobject(false), + m_hasVirtuals(false), + m_isPolymorphic(false), + m_hasNonpublic(false), + m_hasVirtualSlots(false), + m_hasNonPrivateConstructor(false), + m_functionsFixed(false), + m_hasPrivateDestructor(false), + m_forceShellClass(false), + m_hasHashFunction(false), + m_hasEqualsOperator(false), + m_hasCloneOperator(false), + m_isTypeAlias(false), + m_enclosingClass(0), + m_baseClass(0), + m_templateBaseClass(0), + m_extractedInterface(0), + m_primaryInterfaceImplementor(0), + m_typeEntry(0)//, + //m_qDebugStreamFunction(0) + {} + + virtual ~AbstractMetaClass(); + + AbstractMetaClass *extractInterface(); + void fixFunctions(); + + AbstractMetaFunctionList functions() const + { + return m_functions; + } + + void setFunctions(const AbstractMetaFunctionList &functions); + void addFunction(AbstractMetaFunction *function); + bool hasFunction(const AbstractMetaFunction *f) const; + bool hasFunction(const QString &str) const; + bool hasSignal(const AbstractMetaFunction *f) const; + + bool hasConstructors() const; + + void addDefaultConstructor(); + + bool hasNonPrivateConstructor() const + { + return m_hasNonPrivateConstructor; + } + + void setHasNonPrivateConstructor(bool value) + { + m_hasNonPrivateConstructor = value; + } + + bool hasPrivateDestructor() const + { + return m_hasPrivateDestructor; + } + + void setHasPrivateDestructor(bool value) + { + m_hasPrivateDestructor = value; + } + + AbstractMetaFunctionList queryFunctionsByName(const QString &name) const; + AbstractMetaFunctionList queryFunctions(uint query) const; + inline AbstractMetaFunctionList allVirtualFunctions() const; + inline AbstractMetaFunctionList allFinalFunctions() const; + AbstractMetaFunctionList functionsInTargetLang() const; + AbstractMetaFunctionList functionsInShellClass() const; + inline AbstractMetaFunctionList cppInconsistentFunctions() const; + inline AbstractMetaFunctionList cppSignalFunctions() const; + AbstractMetaFunctionList publicOverrideFunctions() const; + AbstractMetaFunctionList virtualOverrideFunctions() const; + AbstractMetaFunctionList virtualFunctions() const; + AbstractMetaFunctionList nonVirtualShellFunctions() const; + AbstractMetaFunctionList implicitConversions() const; + + /** + * Retrives all class' operator overloads that meet + * query crieteria defined with the OperatorQueryOption + * enum. + * /param query composition of OperatorQueryOption enum values + * /return list of operator overload methods that meet the + * query criteria + */ + AbstractMetaFunctionList operatorOverloads(uint query = AllOperators) const; + + bool hasOperatorOverload() const; + bool hasArithmeticOperatorOverload() const; + bool hasBitwiseOperatorOverload() const; + bool hasComparisonOperatorOverload() const; + bool hasLogicalOperatorOverload() const; + bool hasSubscriptOperatorOverload() const; + bool hasAssignmentOperatorOverload() const; + bool hasConversionOperatorOverload() const; + + AbstractMetaFieldList fields() const + { + return m_fields; + } + + void setFields(const AbstractMetaFieldList &fields) + { + m_fields = fields; + } + + void addField(AbstractMetaField *field) + { + m_fields << field; + } + + AbstractMetaEnumList enums() const + { + return m_enums; + } + void setEnums(const AbstractMetaEnumList &enums) + { + m_enums = enums; + } + + void addEnum(AbstractMetaEnum *e) + { + m_enums << e; + } + + AbstractMetaEnum *findEnum(const QString &enumName); + AbstractMetaEnum *findEnumForValue(const QString &enumName); + AbstractMetaEnumValue *findEnumValue(const QString &enumName, AbstractMetaEnum *meta_enum); + + AbstractMetaClassList interfaces() const + { + return m_interfaces; + } + void addInterface(AbstractMetaClass *interface); + void setInterfaces(const AbstractMetaClassList &interface); + + QString fullName() const + { + return package() + "." + name(); + } + + QString name() const; + + QString baseClassName() const + { + return m_baseClass ? m_baseClass->name() : QString(); + } + + AbstractMetaClass *baseClass() const + { + return m_baseClass; + } + + void setBaseClass(AbstractMetaClass *base_class); + + const AbstractMetaClass *enclosingClass() const + { + return m_enclosingClass; + } + + void setEnclosingClass(AbstractMetaClass *cl) + { + m_enclosingClass = cl; + } + + const AbstractMetaClassList& innerClasses() const + { + return m_innerClasses; + } + + void addInnerClass(AbstractMetaClass* cl) + { + m_innerClasses << cl; + } + + void setInnerClasses(AbstractMetaClassList innerClasses) + { + m_innerClasses = innerClasses; + } + + QString package() const + { + return m_typeEntry->targetLangPackage(); + } + + bool isInterface() const + { + return m_typeEntry->isInterface(); + } + + bool isNamespace() const + { + return m_typeEntry->isNamespace(); + } + + bool isQObject() const + { + return m_typeEntry->isQObject(); + } + + bool isQtNamespace() const + { + return isNamespace() && name() == "Qt"; + } + + QString qualifiedCppName() const + { + return m_typeEntry->qualifiedCppName(); + } + + bool hasInconsistentFunctions() const; + bool hasSignals() const; + bool inheritsFrom(const AbstractMetaClass *other) const; + + void setForceShellClass(bool on) + { + m_forceShellClass = on; + } + + bool generateShellClass() const; + + bool hasVirtualSlots() const + { + return m_hasVirtualSlots; + } + + /** + * Says if a class has any virtual functions of its own. + * \return true if the class implements any virtual methods + */ + bool hasVirtualFunctions() const + { + return !isFinal() && m_hasVirtuals; + } + /** + * Says if the class that declares or inherits a virtual function. + * \return true if the class implements or inherits any virtual methods + */ + bool isPolymorphic() const + { + return m_isPolymorphic; + } + bool hasProtectedFunctions() const; + + QList templateArguments() const + { + return m_templateArgs; + } + + void setTemplateArguments(const QList &args) + { + m_templateArgs = args; + } + + bool hasFieldAccessors() const; + + // only valid during metabuilder's run + QStringList baseClassNames() const + { + return m_baseClassNames; + } + + void setBaseClassNames(const QStringList &names) + { + m_baseClassNames = names; + } + + AbstractMetaClass *primaryInterfaceImplementor() const + { + return m_primaryInterfaceImplementor; + } + + void setPrimaryInterfaceImplementor(AbstractMetaClass *cl) + { + m_primaryInterfaceImplementor = cl; + } + + const ComplexTypeEntry *typeEntry() const + { + return m_typeEntry; + } + + ComplexTypeEntry *typeEntry() + { + return m_typeEntry; + } + + void setTypeEntry(ComplexTypeEntry *type) + { + m_typeEntry = type; + } + + void setHasHashFunction(bool on) + { + m_hasHashFunction = on; + } + + bool hasHashFunction() const + { + return m_hasHashFunction; + } +#if 0 + void setToStringCapability(FunctionModelItem fun) + { + m_qDebugStreamFunction = fun; + } + + FunctionModelItem hasToStringCapability() const + { + return m_qDebugStreamFunction; + } +#endif + virtual bool hasDefaultToStringFunction() const; + + void setHasEqualsOperator(bool on) + { + m_hasEqualsOperator = on; + } + + bool hasEqualsOperator() const + { + return m_hasEqualsOperator; + } + + void setHasCloneOperator(bool on) + { + m_hasCloneOperator = on; + } + + bool hasCloneOperator() const + { + return m_hasCloneOperator; + } + + void addPropertySpec(QPropertySpec *spec) + { + m_propertySpecs << spec; + } + + QList propertySpecs() const + { + return m_propertySpecs; + } + + QPropertySpec *propertySpecForRead(const QString &name) const; + QPropertySpec *propertySpecForWrite(const QString &name) const; + QPropertySpec *propertySpecForReset(const QString &name) const; + + QList referenceCounts() const; + + void setEqualsFunctions(const AbstractMetaFunctionList &lst) + { + m_equalsFunctions = lst; + } + + AbstractMetaFunctionList equalsFunctions() const + { + return m_equalsFunctions; + } + + void setNotEqualsFunctions(const AbstractMetaFunctionList &lst) + { + m_nequalsFunctions = lst; + } + + AbstractMetaFunctionList notEqualsFunctions() const + { + return m_nequalsFunctions; + } + + void setLessThanFunctions(const AbstractMetaFunctionList &lst) + { + m_lessThanFunctions = lst; + } + + AbstractMetaFunctionList lessThanFunctions() const + { + return m_lessThanFunctions; + } + + void setGreaterThanFunctions(const AbstractMetaFunctionList &lst) + { + m_greaterThanFunctions = lst; + } + + AbstractMetaFunctionList greaterThanFunctions() const + { + return m_greaterThanFunctions; + } + + void setLessThanEqFunctions(const AbstractMetaFunctionList &lst) + { + m_lessThanEqFunctions = lst; + } + + AbstractMetaFunctionList lessThanEqFunctions() const + { + return m_lessThanEqFunctions; + } + + void setGreaterThanEqFunctions(const AbstractMetaFunctionList &lst) + { + m_greaterThanEqFunctions = lst; + } + + AbstractMetaFunctionList greaterThanEqFunctions() const + { + return m_greaterThanEqFunctions; + } + + void sortFunctions(); + + const AbstractMetaClass *templateBaseClass() const + { + return m_templateBaseClass; + } + + void setTemplateBaseClass(const AbstractMetaClass *cls) + { + m_templateBaseClass = cls; + } + + void setTypeAlias(bool typeAlias) + { + m_isTypeAlias = typeAlias; + } + + bool isTypeAlias() const + { + return m_isTypeAlias; + } + +private: + uint m_namespace : 1; + uint m_qobject : 1; + uint m_hasVirtuals : 1; + uint m_isPolymorphic : 1; + uint m_hasNonpublic : 1; + uint m_hasVirtualSlots : 1; + uint m_hasNonPrivateConstructor : 1; + uint m_functionsFixed : 1; + uint m_hasPrivateDestructor : 1; + uint m_forceShellClass : 1; + uint m_hasHashFunction : 1; + uint m_hasEqualsOperator : 1; + uint m_hasCloneOperator : 1; + uint m_isTypeAlias : 1; + uint m_reserved : 18; + + const AbstractMetaClass *m_enclosingClass; + AbstractMetaClass *m_baseClass; + const AbstractMetaClass *m_templateBaseClass; + AbstractMetaFunctionList m_functions; + AbstractMetaFieldList m_fields; + AbstractMetaEnumList m_enums; + AbstractMetaClassList m_interfaces; + AbstractMetaClass *m_extractedInterface; + AbstractMetaClass *m_primaryInterfaceImplementor; + QList m_propertySpecs; + AbstractMetaFunctionList m_equalsFunctions; + AbstractMetaFunctionList m_nequalsFunctions; + AbstractMetaClassList m_innerClasses; + + AbstractMetaFunctionList m_lessThanFunctions; + AbstractMetaFunctionList m_greaterThanFunctions; + AbstractMetaFunctionList m_lessThanEqFunctions; + AbstractMetaFunctionList m_greaterThanEqFunctions; + + QStringList m_baseClassNames; + QList m_templateArgs; + ComplexTypeEntry *m_typeEntry; +// FunctionModelItem m_qDebugStreamFunction; +}; + +class QPropertySpec +{ +public: + QPropertySpec(const TypeEntry *type) + : m_type(type), + m_index(-1) + {} + + const TypeEntry *type() const + { + return m_type; + } + + QString name() const + { + return m_name; + } + + void setName(const QString &name) + { + m_name = name; + } + + QString read() const + { + return m_read; + } + + void setRead(const QString &read) + { + m_read = read; + } + + QString write() const + { + return m_write; + } + + void setWrite(const QString &write) + { + m_write = write; + } + + QString designable() const + { + return m_designable; + } + + void setDesignable(const QString &designable) + { + m_designable = designable; + } + + QString reset() const + { + return m_reset; + } + + void setReset(const QString &reset) + { + m_reset = reset; + } + + int index() const + { + return m_index; + } + + void setIndex(int index) + { + m_index = index; + } + +private: + QString m_name; + QString m_read; + QString m_write; + QString m_designable; + QString m_reset; + const TypeEntry *m_type; + int m_index; +}; + +inline AbstractMetaFunctionList AbstractMetaClass::allVirtualFunctions() const +{ + return queryFunctions(VirtualFunctions | NotRemovedFromTargetLang); +} + +inline AbstractMetaFunctionList AbstractMetaClass::allFinalFunctions() const +{ + return queryFunctions(FinalInTargetLangFunctions + | FinalInCppFunctions + | NotRemovedFromTargetLang); +} + +inline AbstractMetaFunctionList AbstractMetaClass::cppInconsistentFunctions() const +{ + return queryFunctions(Inconsistent + | NormalFunctions + | Visible + | NotRemovedFromTargetLang); +} + +inline AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const +{ + return queryFunctions(Signals + | Visible + | NotRemovedFromTargetLang); +} + +#endif // ABSTRACTMETALANG_H diff --git a/apiextractor.cpp b/apiextractor.cpp new file mode 100644 index 000000000..d2ef9ee23 --- /dev/null +++ b/apiextractor.cpp @@ -0,0 +1,307 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "apiextractor.h" +#include +#include +#include + +#include "reporthandler.h" +#include "typesystem.h" +#include "fileout.h" +#include "parser/rpp/pp.h" +#include "abstractmetabuilder.h" +#include "generator.h" +#include "apiextractorversion.h" + +static bool preprocess(const QString& sourceFile, + const QString& targetFile, + const QString& commandLineIncludes); + +ApiExtractor::ApiExtractor(int argc, char** argv) : m_versionHandler(0) +{ + m_programName = argv[0]; + // store args in m_args map + int argNum = 0; + for (int i = 1; i < argc; ++i) { + QString arg(argv[i]); + arg = arg.trimmed(); + if (arg.startsWith("--")) { + int split = arg.indexOf("="); + if (split > 0) + m_args[arg.mid(2).left(split-2)] = arg.mid(split + 1).trimmed(); + else + m_args[arg.mid(2)] = QString(); + } else if (arg.startsWith("-")) { + m_args[arg.mid(1)] = QString(); + } else { + argNum++; + m_args[QString("arg-%1").arg(argNum)] = arg; + } + } + + // Environment TYPESYSTEMPATH + QString envTypesystemPaths = getenv("TYPESYSTEMPATH"); + TypeDatabase::instance()->addTypesystemPath(envTypesystemPaths); + ReportHandler::setContext("ApiExtractor"); +} + +ApiExtractor::~ApiExtractor() +{ + qDeleteAll(m_generators); +} + +void ApiExtractor::addGenerator(Generator* generator) +{ + m_generators << generator; +} + +bool ApiExtractor::parseGeneralArgs() +{ + // set debug level + if (m_args.contains("silent")) { + ReportHandler::setSilent(true); + } else if (m_args.contains("debug-level")) { + QString level = m_args.value("debug-level"); + if (level == "sparse") + ReportHandler::setDebugLevel(ReportHandler::SparseDebug); + else if (level == "medium") + ReportHandler::setDebugLevel(ReportHandler::MediumDebug); + else if (level == "full") + ReportHandler::setDebugLevel(ReportHandler::FullDebug); + } + + if (m_args.contains("no-suppress-warnings")) { + TypeDatabase *db = TypeDatabase::instance(); + db->setSuppressWarnings(false); + } + + if (m_args.contains("dummy")) + FileOut::dummy = true; + + if (m_args.contains("diff")) + FileOut::diff = true; + + if (m_args.count() == 1) + return false; + + if (m_args.contains("typesystem-paths")) + TypeDatabase::instance()->addTypesystemPath(m_args.value("typesystem-paths")); + + m_globalHeaderFileName = m_args.value("arg-1"); + m_typeSystemFileName = m_args.value("arg-2"); + if (m_args.contains("arg-3")) + return false; + + if (m_globalHeaderFileName.isEmpty() || m_typeSystemFileName.isEmpty()) + return false; + return true; +} + +int ApiExtractor::exec() +{ + if (m_args.contains("version")) { + if (m_versionHandler) + m_versionHandler("ApiExtractor v" APIEXTRACTOR_VERSION); + else + std::cout << m_programName << " using ApiExtractor v" APIEXTRACTOR_VERSION << std::endl; + return 0; + } else if (!parseGeneralArgs()) { + printUsage(); + return 1; + } + + QLatin1String ppFileName(".preprocessed.tmp"); + + if (!TypeDatabase::instance()->parseFile(m_typeSystemFileName)) + std::cerr << "Cannot parse file: " << qPrintable(m_typeSystemFileName); + + if (!preprocess(m_globalHeaderFileName, ppFileName, m_args.value("include-paths"))) { + std::cerr << "Preprocessor failed on file: " << qPrintable(m_globalHeaderFileName); + return 1; + } + + QString licenseComment; + if (m_args.contains("license-file") && !m_args.value("license-file").isEmpty()) { + QString license_filename = m_args.value("license-file"); + if (QFile::exists(license_filename)) { + QFile license_file(license_filename); + if (license_file.open(QIODevice::ReadOnly)) + licenseComment = license_file.readAll(); + } else { + std::cerr << "Couldn't find the file containing the license heading: "; + std::cerr << qPrintable(license_filename); + return 1; + } + } + + AbstractMetaBuilder builder; + QFile ppFile(ppFileName); + builder.build(&ppFile); + + QString outputDirectory = m_args.contains("output-directory") ? m_args["output-directory"] : "out"; + bool docOnly = m_args.contains("documentation-only"); + foreach (Generator* g, m_generators) { + bool docGen = g->type() == Generator::DocumentationType; + bool missingDocInfo = m_args["library-source-dir"].isEmpty() + || m_args["documentation-data-dir"].isEmpty(); + if ((!docGen && docOnly) || (docGen && missingDocInfo)) { + std::cout << "Skipping " << g->name() << std::endl; + continue; + } + g->setOutputDirectory(outputDirectory); + g->setLicenseComment(licenseComment); + g->setBuilder(&builder); + std::cout << "Running " << g->name() << std::endl; + if (g->prepareGeneration(m_args)) + g->generate(); + } + + std::cout << "Done, " << ReportHandler::warningCount(); + std::cout << " warnings (" << ReportHandler::suppressedCount() << " known issues)"; + std::cout << std::endl; + return 0; +} + +static void printOptions(QTextStream& s, const QMap& options) { + QMap::const_iterator it = options.constBegin(); + s.setFieldAlignment(QTextStream::AlignLeft); + for (; it != options.constEnd(); ++it) { + s << " --"; + s.setFieldWidth(38); + s << it.key() << it.value(); + s.setFieldWidth(0); + s << endl; + } +} + +void ApiExtractor::printUsage() +{ + #if defined(Q_OS_WIN32) + #define PATHSPLITTER ";" + #else + #define PATHSPLITTER ":" + #endif + QTextStream s(stdout); + s << "Usage:\n " + << m_programName << " [options] header-file typesystem-file\n\n" + "General options:\n"; + QMap generalOptions; + generalOptions.insert("debug-level=[sparse|medium|full]", "Set the debug level"); + generalOptions.insert("silent", "Avoid printing any message"); + generalOptions.insert("help", "Display this help and exit"); + generalOptions.insert("no-suppress-warnings", "Show all warnings"); + generalOptions.insert("output-directory=[dir]", "The directory where the generated files will be written"); + generalOptions.insert("include-paths=[" PATHSPLITTER "" PATHSPLITTER "...]", "Include paths used by the C++ parser"); + generalOptions.insert("typesystem-paths=[" PATHSPLITTER "" PATHSPLITTER "...]", "Paths used when searching for typesystems"); + generalOptions.insert("documentation-only", "Do not generates any code, just the documentation"); + generalOptions.insert("license-file=[licensefile]", "File used for copyright headers of generated files"); + generalOptions.insert("version", "Output version information and exit"); + printOptions(s, generalOptions); + + foreach (Generator* generator, m_generators) { + QMap options = generator->options(); + if (!options.isEmpty()) { + s << endl << generator->name() << " options:\n"; + printOptions(s, generator->options()); + } + } +} + + +static bool preprocess(const QString& sourceFile, + const QString& targetFile, + const QString& commandLineIncludes) +{ + rpp::pp_environment env; + rpp::pp preprocess(env); + + rpp::pp_null_output_iterator null_out; + + const char *ppconfig = ":/trolltech/generator/pp-qt-configuration"; + + QFile file(ppconfig); + if (!file.open(QFile::ReadOnly)) { + std::cerr << "Preprocessor configuration file not found " << ppconfig << std::endl; + return false; + } + + QByteArray ba = file.readAll(); + file.close(); + preprocess.operator()(ba.constData(), ba.constData() + ba.size(), null_out); + + QStringList includes; + +#if defined(Q_OS_WIN32) + char *pathSplitter = const_cast(";"); +#else + char *pathSplitter = const_cast(":"); +#endif + + // Environment INCLUDE + QString includePath = getenv("INCLUDE"); + if (!includePath.isEmpty()) + includes += includePath.split(pathSplitter); + + // Includes from the command line + if (!commandLineIncludes.isEmpty()) + includes += commandLineIncludes.split(pathSplitter); + + includes << QLatin1String("."); + includes << QLatin1String("/usr/include"); + + foreach (QString include, includes) + preprocess.push_include_path(QDir::convertSeparators(include).toStdString()); + + QString currentDir = QDir::current().absolutePath(); + QFileInfo sourceInfo(sourceFile); + if (!sourceInfo.exists()) { + std::cerr << "File not found " << qPrintable(sourceFile) << std::endl; + return false; + } + 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)) { + std::cerr << "Failed to write preprocessed file: " << qPrintable(targetFile) << std::endl; + return false; + } + + f.write(result.c_str(), result.length()); + + return true; +} + diff --git a/apiextractor.h b/apiextractor.h new file mode 100644 index 000000000..301e8cdd3 --- /dev/null +++ b/apiextractor.h @@ -0,0 +1,63 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef APIEXTRACTOR_H +#define APIEXTRACTOR_H + +#include +#include +#include + +class Generator; + +class ApiExtractor +{ +public: + ApiExtractor(int argc, char** argv); + ~ApiExtractor(); + + void addGenerator(Generator* generator); + void setVersionHandler(void (*versionHandler)(const char*)) + { + m_versionHandler = versionHandler; + } + + int exec(); + +private: + QLinkedList m_generators; + QMap m_args; + QString m_typeSystemFileName; + QString m_globalHeaderFileName; + const char* m_programName; + void (*m_versionHandler)(const char*); + + bool parseGeneralArgs(); + void printUsage(); + + // disable copy + ApiExtractor(const ApiExtractor&); + ApiExtractor& operator=(const ApiExtractor&); +}; + +#endif // APIEXTRACTOR_H diff --git a/apiextractor.pc.in b/apiextractor.pc.in new file mode 100644 index 000000000..07896a1a0 --- /dev/null +++ b/apiextractor.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@/bin +libdir=@CMAKE_INSTALL_PREFIX@/lib +includedir=@CMAKE_INSTALL_PREFIX@/include + +Name: apiextractor +Description: Qt4 Binding Generator library +Requires: QtCore +Version: @apiextractor_VERSION@ +Libs: -L${libdir} -lapiextractor +Cflags: -I${includedir}/apiextractor diff --git a/apiextractorversion.h.in b/apiextractorversion.h.in new file mode 100644 index 000000000..45ce51e83 --- /dev/null +++ b/apiextractorversion.h.in @@ -0,0 +1,4 @@ +#ifndef APIEXTRACTORVERSION_H +#define APIEXTRACTORVERSION_H +#define APIEXTRACTOR_VERSION "@apiextractor_VERSION@" +#endif diff --git a/asttoxml.cpp b/asttoxml.cpp new file mode 100644 index 000000000..e884835c0 --- /dev/null +++ b/asttoxml.cpp @@ -0,0 +1,151 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "asttoxml.h" +#include "parser/control.h" +#include "parser/parser.h" +#include "parser/binder.h" + + +#include +#include +#include +#include + +void astToXML(QString name) +{ + QFile file(name); + + if (!file.open(QFile::ReadOnly)) + return; + + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + QByteArray contents = stream.readAll().toUtf8(); + file.close(); + + Control control; + Parser p(&control); + pool __pool; + + TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool); + + CodeModel model; + Binder binder(&model, p.location()); + FileModelItem dom = binder.run(ast); + + QFile outputFile; + if (!outputFile.open(stdout, QIODevice::WriteOnly)) + return; + + QXmlStreamWriter s(&outputFile); + s.setAutoFormatting(true); + + s.writeStartElement("code"); + + QHash namespaceMap = dom->namespaceMap(); + foreach (NamespaceModelItem item, namespaceMap.values()) + writeOutNamespace(s, item); + + QHash typeMap = dom->classMap(); + foreach (ClassModelItem item, typeMap.values()) + writeOutClass(s, item); + + s.writeEndElement(); +} + + +void writeOutNamespace(QXmlStreamWriter &s, NamespaceModelItem &item) +{ + s.writeStartElement("namespace"); + s.writeAttribute("name", item->name()); + + QHash namespaceMap = item->namespaceMap(); + foreach (NamespaceModelItem item, namespaceMap.values()) + writeOutNamespace(s, item); + + QHash typeMap = item->classMap(); + foreach (ClassModelItem item, typeMap.values()) + writeOutClass(s, item); + + QHash enumMap = item->enumMap(); + foreach (EnumModelItem item, enumMap.values()) + writeOutEnum(s, item); + + s.writeEndElement(); +} + +void writeOutEnum(QXmlStreamWriter &s, EnumModelItem &item) +{ + QString qualifiedName = item->qualifiedName().join("::"); + s.writeStartElement("enum"); + s.writeAttribute("name", qualifiedName); + + EnumeratorList enumList = item->enumerators(); + for (int i = 0; i < enumList.size() ; i++) { + s.writeStartElement("enumerator"); + if (!enumList[i]->value().isEmpty()) + s.writeAttribute("value", enumList[i]->value()); + s.writeCharacters(enumList[i]->name()); + + s.writeEndElement(); + } + s.writeEndElement(); +} + +void writeOutFunction(QXmlStreamWriter &s, FunctionModelItem &item) +{ + QString qualifiedName = item->qualifiedName().join("::"); + s.writeStartElement("function"); + s.writeAttribute("name", qualifiedName); + + ArgumentList arguments = item->arguments(); + for (int i = 0; i < arguments.size() ; i++) { + s.writeStartElement("argument"); + s.writeAttribute("type", arguments[i]->type().qualifiedName().join("::")); + s.writeEndElement(); + } + s.writeEndElement(); +} + +void writeOutClass(QXmlStreamWriter &s, ClassModelItem &item) +{ + QString qualifiedName = item->qualifiedName().join("::"); + s.writeStartElement("class"); + s.writeAttribute("name", qualifiedName); + + QHash enumMap = item->enumMap(); + foreach (EnumModelItem item, enumMap.values()) + writeOutEnum(s, item); + + QHash functionMap = item->functionMap(); + foreach (FunctionModelItem item, functionMap.values()) + writeOutFunction(s, item); + + QHash typeMap = item->classMap(); + foreach (ClassModelItem item, typeMap.values()) + writeOutClass(s, item); + + s.writeEndElement(); +} + diff --git a/asttoxml.h b/asttoxml.h new file mode 100644 index 000000000..43c7cc238 --- /dev/null +++ b/asttoxml.h @@ -0,0 +1,40 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef ASTTOXML +#define ASTTOXML + +#include "parser/codemodel.h" + +#include +#include + +void astToXML(const QString name); +void writeOutNamespace(QXmlStreamWriter &s, NamespaceModelItem &item); +void writeOutEnum(QXmlStreamWriter &s, EnumModelItem &item); +void writeOutFunction(QXmlStreamWriter &s, FunctionModelItem &item); +void writeOutClass(QXmlStreamWriter &s, ClassModelItem &item); + + +#endif // ASTTOXML diff --git a/cmake_uninstall.cmake b/cmake_uninstall.cmake new file mode 100644 index 000000000..df95fb9d8 --- /dev/null +++ b/cmake_uninstall.cmake @@ -0,0 +1,21 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..0f70dfc08 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,88 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf _build/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + @echo + @echo "Build finished. The HTML pages are in _build/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml + @echo + @echo "Build finished. The HTML pages are in _build/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in _build/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in _build/qthelp, like this:" + @echo "# qcollectiongenerator _build/qthelp/APIExtractor.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile _build/qthelp/APIExtractor.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + @echo + @echo "Build finished; the LaTeX files are in _build/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes + @echo + @echo "The overview file is in _build/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in _build/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in _build/doctest/output.txt." diff --git a/doc/_static/.gitignore b/doc/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/doc/_static/basic.css b/doc/_static/basic.css new file mode 100644 index 000000000..2509c227f --- /dev/null +++ b/doc/_static/basic.css @@ -0,0 +1,417 @@ +/** + * Sphinx stylesheet -- basic theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 0px; + list-style: none; +} + +div.related li { + float: left; + display: inline; + padding-right:17px; + padding-left:10px; + background-image:url(images/bread_crumb.png); + background-position:right; + background-repeat:no-repeat; +} + +div.related li.right { + float: right; + margin-right: 5px; + padding: 0 0 0 0px; + background-image:none; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +img { + border: 0; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + text-align: left; + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + text-align: left; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +/* -- general body styles --------------------------------------------------- */ + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.docutils td, table.docutils th { + padding: 2px 8px 2px 8px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +/* -- other body styles ----------------------------------------------------- */ + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlight { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.refcount { + color: #060; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} diff --git a/doc/_static/default.css b/doc/_static/default.css new file mode 100644 index 000000000..721ceb71b --- /dev/null +++ b/doc/_static/default.css @@ -0,0 +1,248 @@ +/** + * Sphinx stylesheet -- default theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: sans-serif; + font-size: 100%; + background-color: #000000; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + background-image:url(images/side_background.jpg); + background-repeat:repeat-y; + background-color:#ffd800; +} + +div.body { + position:relative; + background-color:#fff; + color: #000000; + padding: 0 20px 30px 20px; +} + +div.footer { + color: #ffffff; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ffffff; + text-decoration: underline; +} + +div.related { + background-image:url(images/top_background.jpg); + background-repeat:repeat-x; + background-color: #d7aa00; + line-height:33px; + height:33px; + color: #000000; +} + +div.related a { + color: #000000; +} + +div.related img { + padding-top:3px; +} + +div.sphinxsidebar { +} + +div.sphinxsidebar h3 { + font-family: Arial, Verdana, sans-serif; + color: #000000; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: #000000; +} + +div.sphinxsidebar h4 { + font-family: Arial, Verdana, sans-serif; + color: #000000; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: #ffffff; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: #ffffff; +} + +div#searchbox p.searchtip { + color:#000000; + font-size:90%; + padding-top:50px; +} + +div#searchbox { + background-image:url(images/background_search.jpg); + background-repeat:no-repeat; + background-position:center; + border:none; +} + +div.sphinxsidebar a { + color: #009491; +} + + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #009491; + text-decoration: underline; +} + +a:hover { + text-decoration: underline; +} + +div.body p, div.body dd, div.body li { + text-align: left; + line-height: 130%; +} + +div.body h1 { + font-family: Arial, Verdana, sans-serif; + background-color: #f2f2f2; + font-weight: normal; + color: #20435c; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Arial, Verdana, Helvetica, sans-serif; + font-size:12px; + font-weight:normal; + border-left-width: 1px; + border-right-width: 1px; + border-top-width: 1px; + border-bottom-width: 2px; + border-style: solid; + border-left-color: #b1b1b1; + border-right-color: #b1b1b1; + border-top-color: #b1b1b1; + border-bottom-color: #009491; + background-color: #e0e0e0; + padding-left:5px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 120%; } +div.body h3 { font-size: 115%; } +div.body h4 { font-size: 110%; } +div.body h5 { font-size: 105%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: left; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +input[type=text]{ + background-color: #009491; + font: 11px verdana, arial, helvetica, sans-serif; + color:#FFFFFF; + width: 150px; + height: 18px; + border: 1px solid #009491; + margin-left:13px; + margin-top:15px; + margin-bottom:4px; + border:none; +} + +pre { + padding: 5px; + background-color: #eeffcc; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} diff --git a/doc/_static/images/._background_search.jpg b/doc/_static/images/._background_search.jpg new file mode 100755 index 000000000..d5c689c31 Binary files /dev/null and b/doc/_static/images/._background_search.jpg differ diff --git a/doc/_static/images/._bread_crumb.png b/doc/_static/images/._bread_crumb.png new file mode 100755 index 000000000..46b8591c6 Binary files /dev/null and b/doc/_static/images/._bread_crumb.png differ diff --git a/doc/_static/images/._button_search.jpg b/doc/_static/images/._button_search.jpg new file mode 100755 index 000000000..d5c689c31 Binary files /dev/null and b/doc/_static/images/._button_search.jpg differ diff --git a/doc/_static/images/._side_background.jpg b/doc/_static/images/._side_background.jpg new file mode 100755 index 000000000..a79b91c97 Binary files /dev/null and b/doc/_static/images/._side_background.jpg differ diff --git a/doc/_static/images/._top_background.jpg b/doc/_static/images/._top_background.jpg new file mode 100755 index 000000000..d5c689c31 Binary files /dev/null and b/doc/_static/images/._top_background.jpg differ diff --git a/doc/_static/images/background_search.jpg b/doc/_static/images/background_search.jpg new file mode 100644 index 000000000..c0481c561 Binary files /dev/null and b/doc/_static/images/background_search.jpg differ diff --git a/doc/_static/images/bg.jpg b/doc/_static/images/bg.jpg new file mode 100644 index 000000000..2ceb19583 Binary files /dev/null and b/doc/_static/images/bg.jpg differ diff --git a/doc/_static/images/bread_crumb.png b/doc/_static/images/bread_crumb.png new file mode 100644 index 000000000..f7ebd20e4 Binary files /dev/null and b/doc/_static/images/bread_crumb.png differ diff --git a/doc/_static/images/button_search.png b/doc/_static/images/button_search.png new file mode 100644 index 000000000..0160b81ab Binary files /dev/null and b/doc/_static/images/button_search.png differ diff --git a/doc/_static/images/side_background.jpg b/doc/_static/images/side_background.jpg new file mode 100644 index 000000000..6e6667542 Binary files /dev/null and b/doc/_static/images/side_background.jpg differ diff --git a/doc/_static/images/top_background.jpg b/doc/_static/images/top_background.jpg new file mode 100644 index 000000000..aafe1f72e Binary files /dev/null and b/doc/_static/images/top_background.jpg differ diff --git a/doc/_templates/index.html b/doc/_templates/index.html new file mode 100644 index 000000000..00a829ccc --- /dev/null +++ b/doc/_templates/index.html @@ -0,0 +1,26 @@ +{% extends "layout.html" %} +{% set title = 'Overview' %} +{% block body %} +

API Extractor {{ version }}

+ +

API Extractor is a tool that eases the development of bindings of Qt-based libraries for high + level languages by automating most of the process. + +

API Extractor is based on the + QtScriptGenerator project.

+ +

Documentation

+ + + +
+ + + + +
+ +{% endblock %} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html new file mode 100644 index 000000000..12fed4d0f --- /dev/null +++ b/doc/_templates/layout.html @@ -0,0 +1,34 @@ +{% extends "!layout.html" %} +{% block rootrellink %} + +
  • {{ shorttitle }}{{ reldelim1 }}
  • +{% endblock %} +{% set reldelim1 = '' %} +{% block extrahead %} + +{{ super() }} +{% endblock %} + +{% block sidebarsearch %} + + +{% endblock %} + diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000..77a4122cd --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# +# ApiExtractor documentation build configuration file, created by +# sphinx-quickstart on Wed Apr 22 15:04:20 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.ifconfig', 'sphinx.ext.refcounting', 'sphinx.ext.coverage'] + +rst_epilog = """ +.. |project| replace:: API Extractor +""" + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +source_encoding = 'utf-8' + +# The master toctree document. +#master_doc = 'contents' + +# General information about the project. +project = u'API Extractor' +copyright = u'2009, Nokia Corporation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.2' +# The full version, including alpha/beta/rc tags. +release = '0.2' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = { +#} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = { '' : ''} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +html_additional_pages = { 'index' : 'index.html'} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +html_use_index = True + +# If true, the index is split into individual pages for each letter. +html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'apiextractor.tex', u'API Extractor Documentation', + u'Nokia Corporation', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/doc/contents.rst b/doc/contents.rst new file mode 100644 index 000000000..88362d38d --- /dev/null +++ b/doc/contents.rst @@ -0,0 +1,8 @@ +Table of contents +***************** +.. toctree:: + :numbered: + :maxdepth: 3 + + overview.rst + typesystem.rst diff --git a/doc/dependency-apiextractor.svg b/doc/dependency-apiextractor.svg new file mode 100644 index 000000000..6bec8b5a8 --- /dev/null +++ b/doc/dependency-apiextractor.svg @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + Boost + Qt Software + INdT/Nokia + + + + + + + + Qt 4.5 + 4.5 + headers and libraries - compile-time and run-time + GNU General Public License v3 /GNU Lesser General Public Licence v2.1 + + + + libapiextractor + 0.2 + headers and libraries - compile-time and run-time + LGPL version 2.1 + + + + boost::graph + 1.38.0 + headers and libraries - compile-time and run-time + Boost Software License 1.0 + + + diff --git a/doc/overview.rst b/doc/overview.rst new file mode 100644 index 000000000..471e2439b --- /dev/null +++ b/doc/overview.rst @@ -0,0 +1,15 @@ +.. _gen-overview: + +********************** +API Extractor Overview +********************** + +The **API Extractor** library is used by the binding generator to parse headers +of a given library and merge this data with information provided by +typesystem (XML) files, resulting in a representation of how the API should be +exported to the chosen target language. The generation of source code for the +bindings is performed by specific generators using the API Extractor library. + +The API Extractor is based on QtScriptGenerator_ codebase. + +.. _QtScriptGenerator: http://labs.trolltech.com/page/Projects/QtScript/Generator diff --git a/doc/typesystem.rst b/doc/typesystem.rst new file mode 100644 index 000000000..19c46aeb1 --- /dev/null +++ b/doc/typesystem.rst @@ -0,0 +1,810 @@ +The API Extractor Type System +***************************** + +The typesystem is used by a binding generator to map a C++ library API onto +a higher level language. + +The typesystem specification is a handwritten XML document listing the types +that will be available in the generated target language API; types that are not +declared in the specification will be ignored along with everything depending on +them. In addition, it is possible to manipulate and modify types and functions. +It is even possible to use the typesystem specification to inject arbitrary +code into the source files, such as an extra member function. + +The typesystem specification is passed as an argument to the generator. + +Below there is a complete reference guide to the various nodes of the typesystem. +For usage examples, take a look at the typesystem files used to generate the Qt +Python bindings. These files can be found in the data directory of the PySide +package. + +.. _specifying-types: + +Specifying Types +---------------- + +typesystem +^^^^^^^^^^ + + This is the root node containing all the type system information. It can + have a number of attributes, described below. + + .. code-block:: xml + + + + + The **package** attribute is a string describing the package to be used, + e.g. "QtCore". + The *optional* **default-superclass** attribute is the canonical C++ base class + name of all objects, e.g., "object". + +load-typesystem +^^^^^^^^^^^^^^^ + + The load-typesystem node specifies which type systems to load when mapping + multiple libraries to another language or basing one library on another, and + it is a child of the typesystem node. + + .. code-block:: xml + + + + + + The **name** attribute is the filename of the typesystem to load, the + **generate** attribute specifies whether code should be generated or not. The + later must be specified when basing one library on another, making the generator + able to understand inheritance hierarchies, primitive mapping, parameter types + in functions, etc. + + Most libraries will be based on both the QtCore and QtGui modules, in which + case code generation for these libraries will be disabled. + + +rejection +^^^^^^^^^ + + The rejection node rejects the given class, or the specified function or + field, and it is a child of the typesystem node. + + .. code-block:: xml + + + + + + The **class** attribute is the C++ class name of the class to reject. Use the + *optional* **function-name** or **field-name** attributes to reject a particular + function or field. Note that the **field-name** and **function-name** cannot + be specified at the same time. To remove all occurrences of a given field or + function, set the class attribute to \*. You can use an empty class field + to denote a global function. + + +primitive-type +^^^^^^^^^^^^^^ + + The primitive-type node describes how a primitive type is mapped from C++ to + the target language, and is a child of the typesystem node. Note that most + primitives are already specified in the QtCore typesystem. + + .. code-block:: xml + + + + + + The **name** attribute is the name of the primitive in C++, the optimal + **target-name** attribute is the name of the primitive type in the target + language. If the later two attributes are not specified their default value + will be the same as the **name** attribute. + + If the *optional* **preferred-conversion** attribute is set to *no*, it + indicates that this version of the primitive type is not the preferred C++ + equivalent of the target language type. For example, in Python both "qint64" + and "long long" become "long" but we should prefer the "qint64" version. For + this reason we mark "long long" with preferred-conversion="no". + +namespace-type +^^^^^^^^^^^^^^ + + The namespace-type node maps the given C++ namespace to the target language, + and it is a child of the typesystem node. Note that within namespaces, the + generator only supports enums (i.e., no functions or classes). + + .. code-block:: xml + + + + + + The **name** attribute is the name of the namespace, e.g., "Qt". The **package** + attribute can be used to override the package of the type system. + +enum-type +^^^^^^^^^ + + The enum-type node maps the given enum from C++ to the target language, + and it is a child of the typesystem node. Use the reject-enum-value to + reject values. + + .. code-block:: xml + + + + + + The **name** attribute is the fully qualified C++ name of the enum + (e.g.,"Qt::FillRule"). If the *optional* **flags** attribute is set to *yes* + (the default is *no*), the generator will expect an existing QFlags for the + given enum type. The **lower-bound** and **upper-bound** attributes are used + to specify runtime bounds checking for the enum value. The value must be a + compilable target language statement, such as "QGradient.Spread.PadSpread" + (taking again Python as an example). If the **force-integer** attribute is + set to *yes* (the default is *no*), the generated target language code will + use the target language integers instead of enums. And finally, the + **extensible** attribute specifies whether the given enum can be extended + with user values (the default is *no*). + + +value-type +^^^^^^^^^^ + + The value-type node indicates that the given C++ type is mapped onto the target + language as a value type. This means that it is an object passed by value on C++, + i.e. it is stored in the function call stack. It is a child of the typesystem node. + + .. code-block:: xml + + + + + + The **name** attribute is the fully qualified C++ class name, such as + "QMatrix" or "QPainterPath::Element". The **copyable** attribute is used to + force or not specify if this type is copyable. The *optional* **hash-function** + attribute informs the function name of a hash function for the type. + + +object-type +^^^^^^^^^^^ + + The object-type node indicates that the given C++ type is mapped onto the target + language as an object type. This means that it is an object passed by pointer on + C++ and it is stored on the heap. It is a child of the typesystem node. + + .. code-block:: xml + + + + + + The **name** attribute is the fully qualified C++ class name. If there is no + C++ base class, the default-superclass attribute can be used to specify a + superclass for the given type, in the generated target language API. The + **copyable** and **hash-function** attributes are the same as described for + :ref:`value-type`. + + +interface-type +^^^^^^^^^^^^^^ + + The interface-type node indicates that the given class is replaced by an + interface pattern when mapping from C++ to the target language. Using the + interface-type node implicitly makes the given type an object type. + + .. code-block:: xml + + + + + + The **name** attribute is the fully qualified C++ class name. The *optional* + **package** attribute can be used to override the package of the type system. + If there is no C++ base class, the *optional* **default-superclass** attribute + can be used to specify a superclass in the generated target language API, for + the given class. + +suppress-warning +^^^^^^^^^^^^^^^^ + + The generator will generate several warnings which may be irrelevant to the + user. The suppress-warning node suppresses the specified warning, and it is + a child of the typesystem node. + + .. code-block:: xml + + + + + + The **text* attribute is the warning text to suppress, and may contain the * + wildcard (use "" to escape regular expression matching if the warning contain + a regular "*"). + + +template +^^^^^^^^ + + The template node registers a template that can be used to avoid duplicate + code when extending the generated code, and it is a child of the typesystem + node. + + .. code-block:: xml + + + + + + Use the insert-template node to insert the template code (identified by the + template's **name** attribute) into the generated code base. + + + +.. _value-type-requirements: + +Value Type Requirements +------------------------ + +custom-constructor +^^^^^^^^^^^^^^^^^^ + + In some target languages, such as Python, value types are required to have a + copy constructor. If a C++ class without a copy constructor is mapped onto + the target language as a value type, it is possible to provide a custom + constructor using the custom-constructor node which is a child of the + value-type node. + + .. code-block:: xml + + + + // code for custom constructor + + + + The custom constructor's signature becomes: + + T \*name(T \*param-name); + + If not specified the **name** of the constructor becomes + _create() and the **param-name** becomes copy. + + **name** and **param-name** attributes are *optional*. + +custom-destructor +^^^^^^^^^^^^^^^^^ + + When a custom constructor is provided using the custom-constructor node, it is + most likely required to clean up the allocated memory. For that reason, it is + also possible to provide a custom destructor using the custom-destructor node + which is a child of the value-type node. + + .. code-block:: xml + + + + // code for custom destructor + + + + The custom destructor must have the following signature: + + T \*name(T \*param-name); + + If not specified the **name** of the destructor becomes + _delete() and the **param-name** becomes copy. + + **name** and **param-name** attributes are *optional*. + +insert-template +^^^^^^^^^^^^^^^ + + Documented in the :ref:`using-code-templates` + + + +.. _manipulating-object-and-value-types: + +Manipulating Object and Value Types +----------------------------------- + +inject-code +^^^^^^^^^^^ + The inject-code node inserts the given code into the generated code for the + given type or function, and it is a child of the object-type, value-type and + modify-function nodes. + + .. code-block:: xml + + + + // the code + + + + The **class** attribute specifies which module of the generated code that + will be affected by the code injection. The **class** attribute accepts the + following values: + + * native: The c++ code + * target: The binding code + * target-declaration: The code will be injected into the generated header + file containing the c++ wrapper class definition. + + If the **position** attribute is set to *beginning* (the default), the code + is inserted at the beginning of the function. If it is set to *end*, the code + is inserted at the end of the function. + +modify-field +^^^^^^^^^^^^ + + The modify-field node allows you to alter the access privileges for a given + C++ field when mapping it onto the target language, and it is a child of an + object-type or a value-type node. + + .. code-block:: xml + + + + + + The **name** attribute is the name of the field, the *optional* **write** + and **read** attributes specify the field's access privileges in the target + language API (both are set to true by default). + +modify-function +^^^^^^^^^^^^^^^ + + The modify-function node allows you to modify a given C++ function when mapping + it onto the target language, and it is a child of an object-type or a value-type + node. Use the modify-argument node to specify which argument the modification + affects. + + .. code-block:: xml + + + + + + The **signature** attribute is a normalized C++ signature, excluding return + values but including potential const declarations. + + The **remove**, **access** and **rename** attributes are *optional* attributes + for added convenience; they serve the same purpose as the tags remove, access + and rename. + + +include +^^^^^^^ + + Documented in the :ref:`manipulating-namespace-and-interface-types` + + +extra-includes +^^^^^^^^^^^^^^ + + Documented in the :ref:`manipulating-namespace-and-interface-types` + + +insert-template +^^^^^^^^^^^^^^^ + + Documented in the :ref:`using-code-templates` + + +.. _manipulating-namespace-and-interface-types: + +Manipulating Namespace and Interface Types +------------------------------------------ + +extra-includes +^^^^^^^^^^^^^^ + + The extra-includes node contains declarations of additional include files, + and it can be a child of the interface-type, namespace-type, value-type and + object-type nodes. + + The generator automatically tries to read the global header for each type but + sometimes it is required to include extra files in the generated C++ code to + make sure that the code compiles. These files must be listed using include + nodes witin the extra-include node: + + .. code-block:: xml + + + + + + + + The **file-name** attribute is the file to include, such as "QStringList". + The **location** attribute is where the file is located: *global* means that + the file is located in $INCLUDEPATH and will be included using #include <...>, + *local* means that the file is in a local directory and will be included + using #include "...". + + +include +^^^^^^^ + + The include node specifies the name and location of a file that must be + included, and it is a child of the interface-type, namespace-type, value-type, + object-type or extra-includes nodes + + The generator automatically tries to read the global header for each type. Use + the include node to override this behavior, providing an alternative file. The + include node can also be used to specify extra include files. + + .. code-block:: xml + + + + + The **file-name** attribute is the file to include, such as "QStringList". + The **location** attribute is where the file is located: *global* means that + the file is located in $INCLUDEPATH and will be included using #include <...>, + *local* means that the file is in a local directory and will be included + using #include "...". + + +.. _manipulating-enum-types: + +Manipulating Enum Types +----------------------- + +reject-enum-value +^^^^^^^^^^^^^^^^^ + + The reject-enum-value node rejects the enum value specified by the **name** + attribute, and it is a child of the enum-type node. + + .. code-block:: xml + + + + + + This node is used when a C++ enum implementation has several identical numeric + values, some of which are typically obsolete. + + **WARNING:** If the enum has some duplicated value don't forget to remove one + of them. + + +.. _modifying-functions: + +Modifying Functions +------------------- + +modify-argument +^^^^^^^^^^^^^^^ + + The modify-argument node specifies which of the given function's arguments the + modification affects, and is a child of the modify-function node. Use the + remove-argument, replace-default-expression, remove-default-expression, + replace-type, reference-count and define-ownership nodes to specify the details + of the modification. + + .. code-block:: xml + + + + // modifications + + + + Set the **index** attribute to "1" for the first argument, "2" for the second + one and so on. Alternatively, set it to "return" or "this" if you want to + modify the function's return value or the object the function is called upon, + respectively. + + +remove +^^^^^^ + + The remove node removes the given method from the generated target language + API, and it is a child of the modify-function node. + + .. code-block:: xml + + + + + + +access +^^^^^^ + + The access node changes the access privileges of the given function in the + generated target language API, and it is a child of the modify-function node. + + .. code-block:: xml + + + + + + +rename +^^^^^^ + + The rename node changes the name of the given function in the generated target + language API, and it is a child of the modify-function node. + + .. code-block:: xml + + + + + + The **to** attribute is the new name of the function. + + +inject-code +^^^^^^^^^^^ + + Documented in the :ref:`manipulating-object-and-value-types` + + +argument-map +^^^^^^^^^^^^ + + The argument-map node maps a C++ argument name to the argument name used in + the generated target language API, and is a child of the inject-code node + when the later is a child of a modify-function node. + + .. code-block:: xml + + + + + + + + The **index** attribute is an index, starting at 1, indicating the argument + position to which this argument mapping applies. The **meta-name** attribute + is the name used within the code injection to adress the argument at the + given position. + + +.. _modifying-arguments: + +Modifying Arguments +------------------- + +remove-argument +^^^^^^^^^^^^^^^ + + The remove-argument node removes the given argument from the function's + signature, and it is a child of the modify-argument node. + + .. code-block:: xml + + + + + + Typically, when removing an argument, some conversion rules must be specified, + e.g., when converting from the target language to C++. This can be done using + the :ref:`conversion-rule` node. + + +remove-default-expression +^^^^^^^^^^^^^^^^^^^^^^^^^ + + The remove-default-expression node disables the use of the default expression + for the given argument, and it is a child of the modify-argument node. + + .. code-block:: xml + + + + + +replace-default-expression +^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The replace-default-expression node replaces the specified argument with the + expression specified by the **with** attribute, and it is a child of the + modify-argument node. + + .. code-block:: xml + + + + + + +replace-type +^^^^^^^^^^^^ + + The replace-type node replaces the type of the given argument to the one + specified by the **modified-type** attribute, and it is a child of the + modify-argument node. + + .. code-block:: xml + + + + + + If the new type is a class, the **modified-type** attribute must be set to + the fully qualified name (including name of the package as well as the class + name). + + Typically when changing the type of an argument some conversion rules must be + specified. This can be done using the :ref:`conversion-rule` node. + + +define-ownership +^^^^^^^^^^^^^^^^ + + The define-ownership tag indicates that the function changes the ownership + rules of the argument object. The **class** attribute specifies the class of + function where to inject the ownership altering code. The **owner** attribute + specifies the new ownership of the object. It accepts the following values: + + * target: the target language will assume full ownership of the object. + The native resources will be deleted when the target language + object is finalized. + * c++: The native code assumes full ownership of the object. The target + language object will not be garbage collected. + * default: The object will get default ownership, depending on how it + was created. + + .. code-block:: xml + + + + + + +insert-template +^^^^^^^^^^^^^^^ + + Documented in the :ref:`using-code-templates` + + +replace-value +^^^^^^^^^^^^^ + + The **replace-value** attribute lets you replace the return statement of a + function with a fixed string. This attribute can only be used for the + argument at **index** 0, which is always the function's return value. + + .. code-block:: xml + + + + +parent +^^^^^^ + + The **parent** attribute lets you define argument parent which will + take ownership of argument and will destroy the C++ child object when the + parent is destroyed. + + .. code-block:: xml + + + + + + In the **index** argument you must specify the parent argument. The action + *add* creates a parent link between objects, while *remove* will undo the + parentage relationship. + + +.. _using-code-templates: + +Using Code Templates +-------------------- + +insert-template +^^^^^^^^^^^^^^^ + + The insert-template node includes the code template identified by the name + attribute, and it can be a child of the inject-code, conversion-rule, template, + custom-constructor and custom-destructor nodes. + + .. code-block:: xml + + + + + + Use the replace node to modify the template code. + + +replace +^^^^^^^ + + The replace node allows you to modify template code before inserting it into + the generated code, and it can be a child of the insert-template node. + + .. code-block:: xml + + + + + + This node will replace the attribute **from** with the value pointed by + **to**. + + +Manipulating Documentation +-------------------------- + +inject-documentation +^^^^^^^^^^^^^^^^^^^^ + + The inject-documentation node inserts the documentation into the generated + documentation. This node is a child of the object-type, value-type and + modify-function nodes. + + .. code-block:: xml + + + + // the documentation + + + + The **mode** attribute default value is *replace*. + + The **format** attribute specifies when the documentation injection will + occur and it accepts the following values: + + * native: Before XML<->Backend transformation occur, so the injected code *must* be a valid XML. + * target: Before XML<->Backend transformation occur, so the injected code *must* be a valid backend format. + + At the moment the only supported backend is Sphinx. + +modify-documentation +^^^^^^^^^^^^^^^^^^^^ + + The modify-documentation node allows you to change the auto-generated + documentation. API Extractor transforms XML's from qdoc3 (the Qt documentation + tool) into .rst files to be processed later using Sphinx. So you can modify + the XML before the transformation occur. + + .. code-block:: xml + + + + + + The **xpath** attribute is the XPath to the node that you want to modify. diff --git a/docparser.cpp b/docparser.cpp new file mode 100644 index 000000000..ddff5ffe9 --- /dev/null +++ b/docparser.cpp @@ -0,0 +1,152 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include "docparser.h" +#include +#include +#include + +#include +#include +#include + +DocParser::DocParser() +{ + xmlSubstituteEntitiesDefault(1); +} + +DocParser::~DocParser() +{ +} + +QString DocParser::getDocumentation(QXmlQuery& xquery, const QString& query, + const DocModificationList& mods) const +{ + QString doc = execXQuery(xquery, query); + return applyDocModifications(mods, doc); +} + +QString DocParser::execXQuery(QXmlQuery& xquery, const QString& query) const +{ + QString escapedQuery(query); + // XQuery can't have invalid XML characters + escapedQuery.replace("&", "&").replace("<", "<"); + xquery.setQuery(escapedQuery); + if (!xquery.isValid()) { + qWarning() << "Bad XQuery: " << escapedQuery; + return QString(); + } + + QString result; + xquery.evaluateTo(&result); + return result; +} + +namespace +{ + +struct XslResources +{ + xmlDocPtr xmlDoc; + xsltStylesheetPtr xslt; + xmlDocPtr xslResult; + + XslResources() : xmlDoc(0), xslt(0), xslResult(0) {} + + ~XslResources() + { + if (xslt) + xsltFreeStylesheet(xslt); + + if (xslResult) + xmlFreeDoc(xslResult); + + if (xmlDoc) + xmlFreeDoc(xmlDoc); + + xsltCleanupGlobals(); + xmlCleanupParser(); + } +}; + +} // namespace + +QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const +{ + if (mods.isEmpty()) + return xml; + + QString xsl = QLatin1String("\n" + "\n" + " \n" + "\n" + "\n" + "\n" + " \n" + " \n" + "\n" + "\n" + ); + foreach (DocModification mod, mods) { + if (mod.mode() == DocModification::XPathReplace) { + xsl += QLatin1String("") + + mod.code() + QLatin1String("\n"); + } + } + xsl += QLatin1String(""); + + XslResources res; + // Read XML data + QByteArray xmlData = xml.toUtf8(); + res.xmlDoc = xmlParseMemory(xmlData.constData(), xmlData.size()); + if (!res.xmlDoc) + return xml; + + // Read XSL data as a XML file + QByteArray xslData = xsl.toUtf8(); + // xsltFreeStylesheet will delete this pointer + xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size()); + if (!xslDoc) + return xml; + + // Parse XSL data + res.xslt = xsltParseStylesheetDoc(xslDoc); + if (!res.xslt) + return xml; + + // Apply XSL + res.xslResult = xsltApplyStylesheet(res.xslt, res.xmlDoc, 0); + xmlChar* buffer = 0; + int bufferSize; + QString result; + if (!xsltSaveResultToString(&buffer, &bufferSize, res.xslResult, res.xslt)) { + result = QByteArray(reinterpret_cast(buffer), bufferSize); + std::free(buffer); + } else { + result = xml; + } + + Q_ASSERT(result != xml); + return result; +} + diff --git a/docparser.h b/docparser.h new file mode 100644 index 000000000..ecc3204e9 --- /dev/null +++ b/docparser.h @@ -0,0 +1,109 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef DOCPARSER_H +#define DOCPARSER_H + +#include +#include +// #include + +#include "abstractmetalang.h" + +class QDomDocument; +class QDomNode; +class QXmlQuery; + +class DocParser +{ +public: + DocParser(); + virtual ~DocParser(); + virtual void fillDocumentation(AbstractMetaClass* metaClass) = 0; + + /** + * Process and retrieves documentation concerning the entire + * module or library. + * /return object containing module/library documentation information + */ + virtual Documentation retrieveModuleDocumentation() = 0; + + void setDocumentationDataDirectory(const QString& dir) + { + m_docDataDir = dir; + } + + /** + * Informs the location of the XML data generated by the tool + * (e.g.: DoxyGen, qdoc) used to extract the library's documentation + * comment. + * /return the path for the directory containing the XML data created + * from the library's documentation beign parsed. + */ + QString documentationDataDirectory() const + { + return m_docDataDir; + } + + void setLibrarySourceDirectory(const QString& dir) + { + m_libSourceDir = dir; + } + /** + * Informs the location of the library being parsed. The library + * source code is parsed for the documentation comments. + * /return the path for the directory containing the source code of + * the library beign parsed. + */ + QString librarySourceDirectory() const + { + return m_libSourceDir; + } + + void setPackageName(const QString& packageName) + { + m_packageName = packageName; + } + /** + * Retrieves the name of the package (or module or library) being parsed. + * /return the name of the package (module/library) being parsed + */ + QString packageName() const + { + return m_packageName; + } + +protected: + QString getDocumentation(QXmlQuery& xquery, const QString& query, + const DocModificationList& mods) const; + +private: + QString m_packageName; + QString m_docDataDir; + QString m_libSourceDir; + + QString execXQuery(QXmlQuery& xquery, const QString& query) const; + QString applyDocModifications(const DocModificationList& mods, const QString& xml) const; +}; + +#endif // DOCPARSER_H + diff --git a/doxygenparser.cpp b/doxygenparser.cpp new file mode 100644 index 000000000..ccf14a916 --- /dev/null +++ b/doxygenparser.cpp @@ -0,0 +1,151 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "doxygenparser.h" +#include "abstractmetalang.h" +#include "reporthandler.h" + +#include + +namespace +{ + +QString getSectionKindAttr(const AbstractMetaFunction* func) +{ + if (func->isSignal()) { + return QLatin1String("signal"); + } else { + QString kind = func->isPublic() ? QLatin1String("public") : QLatin1String("protected"); + if (func->isStatic()) + kind += QLatin1String("-static"); + else if (func->isSlot()) + kind += QLatin1String("-slot"); + return kind; + } +} + +} + +void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) +{ + if (!metaClass) + return; + + QString doxyFileSuffix; + if (metaClass->enclosingClass()) { + doxyFileSuffix += metaClass->enclosingClass()->name(); + doxyFileSuffix += "_1_1"; // FIXME: Check why _1_1!! + } + doxyFileSuffix += metaClass->name(); + doxyFileSuffix += ".xml"; + + const char* prefixes[] = { "class", "struct", "namespace" }; + const int numPrefixes = sizeof(prefixes); + + QString doxyFilePath; + for (int i = 0; i < numPrefixes; ++i) { + doxyFilePath = documentationDataDirectory() + "./" + prefixes[i] + doxyFileSuffix; + if (QFile::exists(doxyFilePath)) + break; + doxyFilePath.clear(); + } + + if (doxyFilePath.isEmpty()) { + ReportHandler::warning("Can't find doxygen file for class " + + metaClass->name() + ", tried: " + + documentationDataDirectory() + "/{struct|class|namespace}" + + doxyFileSuffix); + return; + } + QXmlQuery xquery; + xquery.setFocus(QUrl(doxyFilePath)); + + // Get class documentation + QString classDoc = getDocumentation(xquery, "/doxygen/compounddef/detaileddescription", + metaClass->typeEntry()->docModifications()); + if (classDoc.isEmpty()) { + ReportHandler::warning("Can't find documentation for class \"" + + metaClass->name() + "\"."); + } + metaClass->setDocumentation(classDoc); + + //Functions Documentation + AbstractMetaFunctionList funcs = metaClass->functionsInTargetLang(); + foreach (AbstractMetaFunction *func, funcs) { + if (!func || func->isPrivate()) + continue; + + QString query = "/doxygen/compounddef/sectiondef"; + // properties + if (func->isPropertyReader() || func->isPropertyWriter() + || func->isPropertyResetter()) { + query += "[@kind=\"property\"]/memberdef/name[text()=\"" + + func->propertySpec()->name() + "\"]"; + } else { // normal methods + QString kind = getSectionKindAttr(func); + query += "[@kind=\"" + kind + "-func\"]/memberdef/name[text()=\"" + + func->originalName() + "\"]"; + + if (func->arguments().isEmpty()) { + QString args = func->isConstant() ? "() const " : "()"; + query += "/../argsstring[text()=\"" + args + "\"]"; + } else { + int i = 1; + foreach (AbstractMetaArgument* arg, func->arguments()) { + QString type; + if (!arg->type()->isPrimitive()) { + query += "/../param[" + QString::number(i) + "]/type/ref[text()=\"" + + arg->type()->name() + "\"]/../.."; + } else { + query += "/../param[" + QString::number(i) + "]/type[text()=\"" + + arg->type()->name() + "\"]/.."; + } + ++i; + } + } + } + query += "/../detaileddescription"; + QString doc = getDocumentation(xquery, query, DocModificationList()); + func->setDocumentation(doc); + } + + //Fields + AbstractMetaFieldList fields = metaClass->fields(); + foreach (AbstractMetaField *field, fields) { + if (field->isPrivate()) + return; + + QString query = "/doxygen/compounddef/sectiondef/memberdef/name[text()=\"" + + field->name() + "\"]/../detaileddescription"; + QString doc = getDocumentation(xquery, query, DocModificationList()); + field->setDocumentation(doc); + } + + //Enums + AbstractMetaEnumList enums = metaClass->enums(); + foreach (AbstractMetaEnum *meta_enum, enums) { + QString query = "/doxygen/compounddef/sectiondef/memberdef[@kind=\"enum\"]/name[text()=\"" + meta_enum->name() + "\"]/../detaileddescription"; + QString doc = getDocumentation(xquery, query, DocModificationList()); + meta_enum->setDocumentation(doc); + } +} diff --git a/doxygenparser.h b/doxygenparser.h new file mode 100644 index 000000000..16d53bdcd --- /dev/null +++ b/doxygenparser.h @@ -0,0 +1,37 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef DOXYGENPARSER_H +#define DOXYGENPARSER_H + +#include "docparser.h" + +class DoxygenParser : public DocParser +{ +public: + DoxygenParser() {} + virtual void fillDocumentation(AbstractMetaClass *metaClass); + virtual Documentation retrieveModuleDocumentation() { return Documentation(); } +}; + +#endif // DOXYGENPARSER_H diff --git a/fileout.cpp b/fileout.cpp new file mode 100644 index 000000000..ac5206bd6 --- /dev/null +++ b/fileout.cpp @@ -0,0 +1,218 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "fileout.h" +#include "reporthandler.h" + +#include +#include +#include + +#include + +bool FileOut::dummy = false; +bool FileOut::diff = false; + +#ifdef Q_OS_LINUX +const char* colorDelete = "\033[31m"; +const char* colorAdd = "\033[32m"; +const char* colorInfo = "\033[36m"; +const char* colorReset = "\033[0m"; +#else +const char* colorDelete = ""; +const char* colorAdd = ""; +const char* colorInfo = ""; +const char* colorReset = ""; +#endif + +FileOut::FileOut(QString n): + name(n), + stream(&tmp), + isDone(false) +{} + +static int* lcsLength(QList a, QList b) +{ + const int height = a.size() + 1; + const int width = b.size() + 1; + + int *res = new int[width * height]; + + for (int row = 0; row < height; row++) + res[width * row] = 0; + + for (int col = 0; col < width; col++) + res[col] = 0; + + for (int row = 1; row < height; row++) { + for (int col = 1; col < width; col++) { + if (a[row-1] == b[col-1]) + res[width * row + col] = res[width * (row-1) + col-1] + 1; + else + res[width * row + col] = qMax(res[width * row + col-1], + res[width * (row-1) + col]); + } + } + return res; +} + +enum Type { + Add, + Delete, + Unchanged +}; + +struct Unit +{ + Unit(Type type, int pos) : + type(type), + start(pos), + end(pos) {} + + Type type; + int start; + int end; + + void print(QList a, QList b) + { + if (type == Unchanged) { + if ((end - start) > 9) { + for (int i = start; i <= start + 2; i++) + std::printf(" %s\n", a[i].data()); + std::printf("%s=\n= %d more lines\n=%s\n", colorInfo, end - start - 6, colorReset); + for (int i = end - 2; i <= end; i++) + std::printf(" %s\n", a[i].data()); + } else { + for (int i = start; i <= end; i++) + std::printf(" %s\n", a[i].data()); + } + } else if (type == Add) { + std::printf("%s", colorAdd); + for (int i = start; i <= end; i++) + std::printf("+ %s\n", b[i].data()); + std::printf("%s", colorReset); + } else if (type == Delete) { + std::printf("%s", colorDelete); + for (int i = start; i <= end; i++) + std::printf("- %s\n", a[i].data()); + std::printf("%s", colorReset); + } + } +}; + +static QList *unitAppend(QList *res, Type type, int pos) +{ + if (!res) { + res = new QList; + res->append(new Unit(type, pos)); + return res; + } + + Unit *last = res->last(); + if (last->type == type) + last->end = pos; + else + res->append(new Unit(type, pos)); + + return res; +} + +static QList *diffHelper(int *lcs, QList a, QList b, int row, int col) +{ + if (row > 0 && col > 0 && (a[row-1] == b[col-1])) { + return unitAppend(diffHelper(lcs, a, b, row - 1, col - 1), Unchanged, row - 1); + } else { + int width = b.size() + 1; + if ((col > 0) + && (row == 0 || lcs[width * row + col-1] >= lcs[width *(row-1) + col])) { + return unitAppend(diffHelper(lcs, a, b, row, col - 1), Add, col - 1); + } else if ((row > 0) + && (col == 0 || lcs[width * row + col-1] < lcs[width *(row-1) + col])) { + return unitAppend(diffHelper(lcs, a, b, row - 1, col), Delete, row - 1); + } + } + delete lcs; + return 0; +} + +static void diff(QList a, QList b) +{ + QList *res = diffHelper(lcsLength(a, b), a, b, a.size(), b.size()); + for (int i = 0; i < res->size(); i++) { + Unit *unit = res->at(i); + unit->print(a, b); + delete(unit); + } + delete(res); +} + + +bool FileOut::done() +{ + Q_ASSERT(!isDone); + isDone = true; + bool fileEqual = false; + QFile fileRead(name); + QFileInfo info(fileRead); + stream.flush(); + QByteArray original; + if (info.exists() && (diff || (info.size() == tmp.size()))) { + if (!fileRead.open(QIODevice::ReadOnly)) { + ReportHandler::warning(QString("failed to open file '%1' for reading") + .arg(fileRead.fileName())); + return false; + } + + original = fileRead.readAll(); + fileRead.close(); + fileEqual = (original == tmp); + } + + if (!fileEqual) { + if (!FileOut::dummy) { + QDir dir(info.absolutePath()); + if (!dir.mkpath(dir.absolutePath())) { + ReportHandler::warning(QString("unable to create directory '%1'") + .arg(dir.absolutePath())); + return false; + } + + QFile fileWrite(name); + if (!fileWrite.open(QIODevice::WriteOnly)) { + ReportHandler::warning(QString("failed to open file '%1' for writing") + .arg(fileWrite.fileName())); + return false; + } + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); + stream.setDevice(&fileWrite); + stream << tmp; + } + if (diff) { + std::printf("%sFile: %s%s\n", colorInfo, qPrintable(name), colorReset); + ::diff(original.split('\n'), tmp.split('\n')); + std::printf("\n"); + } + return true; + } + return false; +} diff --git a/fileout.h b/fileout.h new file mode 100644 index 000000000..7570bbc45 --- /dev/null +++ b/fileout.h @@ -0,0 +1,58 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef FILEOUT_H +#define FILEOUT_H + +#include +#include +#include + +class FileOut : public QObject +{ + Q_OBJECT + +private: + QByteArray tmp; + QString name; + +public: + FileOut(QString name); + ~FileOut() + { + if (!isDone) + done(); + } + + bool done(); + + QTextStream stream; + + static bool dummy; + static bool diff; + +private: + bool isDone; +}; + +#endif // FILEOUT_H diff --git a/generator.cpp b/generator.cpp new file mode 100644 index 000000000..46de409c9 --- /dev/null +++ b/generator.cpp @@ -0,0 +1,146 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "generator.h" +#include "reporthandler.h" +#include "fileout.h" + +#include +#include +#include +#include +#include "abstractmetabuilder.h" + +Generator::Generator() : m_numGenerated(0), m_numGeneratedWritten(0) +{} + +Generator::~Generator() +{ +} + +void Generator::setBuilder(AbstractMetaBuilder* builder) +{ + m_globalEnums = builder->globalEnums(); + m_globalFunctions = builder->globalFunctions(); + m_classes = builder->classes(); + m_primitiveTypes = TypeDatabase::instance()->primitiveTypes(); + m_containerTypes = TypeDatabase::instance()->containerTypes(); + foreach (const AbstractMetaClass* cppClass, m_classes) { + if (m_packageName.isEmpty() + && cppClass->typeEntry()->generateCode() + && !cppClass->package().isEmpty()) { + m_packageName = cppClass->package(); + break; + } + } + // does anyone use this? + m_qmetatypeDeclaredTypenames = builder->qtMetaTypeDeclaredTypeNames(); +} + +void Generator::generate() +{ + if (m_classes.isEmpty()) { + ReportHandler::warning(QString("%1: no classes, skipping") + .arg(metaObject()->className())); + return; + } + + foreach (AbstractMetaClass *cls, m_classes) { + if (!shouldGenerate(cls)) + continue; + + QString fileName = fileNameForClass(cls); + if (fileName.isNull()) + continue; + ReportHandler::debugSparse(QString("generating: %1").arg(fileName)); + + FileOut fileOut(outputDirectory() + '/' + subDirectoryForClass(cls) + '/' + fileName); + generateClass(fileOut.stream, cls); + + if (fileOut.done()) + ++m_numGeneratedWritten; + ++m_numGenerated; + } + finishGeneration(); +} + +bool Generator::shouldGenerate(const AbstractMetaClass* metaClass) const +{ + return metaClass->typeEntry()->codeGeneration() & TypeEntry::GenerateTargetLang; +} + +void Generator::verifyDirectoryFor(const QFile &file) +{ + QDir dir = QFileInfo(file).dir(); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) + ReportHandler::warning(QString("unable to create directory '%1'") + .arg(dir.absolutePath())); + } +} + +bool Generator::hasDefaultConstructor(const AbstractMetaType *type) +{ + QString full_name = type->typeEntry()->qualifiedTargetLangName(); + QString class_name = type->typeEntry()->targetLangName(); + + foreach (const AbstractMetaClass *cls, m_classes) { + if (cls->typeEntry()->qualifiedTargetLangName() == full_name) { + AbstractMetaFunctionList functions = cls->functions(); + foreach (const AbstractMetaFunction *function, functions) { + if (function->arguments().isEmpty() && function->name() == class_name) + return true; + } + return false; + } + } + return false; +} + +void Generator::replaceTemplateVariables(QString &code, const AbstractMetaFunction *func) +{ + const AbstractMetaClass *cpp_class = func->ownerClass(); + code.replace("%TYPE", cpp_class->name()); + + foreach (AbstractMetaArgument *arg, func->arguments()) + code.replace("%" + QString::number(arg->argumentIndex() + 1), arg->argumentName()); + + //template values + code.replace("%RETURN_TYPE", translateType(func->type(), cpp_class)); + code.replace("%FUNCTION_NAME", func->originalName()); + + if (code.contains("%ARGUMENT_NAMES")) { + QString str; + QTextStream aux_stream(&str); + writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments); + code.replace("%ARGUMENT_NAMES", str); + } + + if (code.contains("%ARGUMENTS")) { + QString str; + QTextStream aux_stream(&str); + writeFunctionArguments(aux_stream, func, Generator::SkipDefaultValues | Generator::SkipRemovedArguments); + code.replace("%ARGUMENTS", str); + } +} + diff --git a/generator.h b/generator.h new file mode 100644 index 000000000..0be9e6c73 --- /dev/null +++ b/generator.h @@ -0,0 +1,358 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include +#include +#include "abstractmetalang.h" + +class AbstractMetaBuilder; +class QFile; + +/** + * Base class for all generators. The default implementations does nothing, + * you must subclass this to create your own generators. + */ +class Generator : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString outputDirectory READ outputDirectory WRITE setOutputDirectory); + +public: + enum GeneratorType { + CodeType, + DocumentationType + }; + /// Optiosn used around the generator code + enum Option { + NoOption = 0x00000000, + BoxedPrimitive = 0x00000001, + ExcludeConst = 0x00000002, + ExcludeReference = 0x00000004, + UseNativeIds = 0x00000008, + + EnumAsInts = 0x00000010, + SkipName = 0x00000020, + NoCasts = 0x00000040, + SkipReturnType = 0x00000080, + OriginalName = 0x00000100, + ShowStatic = 0x00000200, + UnderscoreSpaces = 0x00000400, + ForceEnumCast = 0x00000800, + ArrayAsPointer = 0x00001000, + VirtualCall = 0x00002000, + SkipTemplateParameters = 0x00004000, + SkipAttributes = 0x00008000, + OriginalTypeDescription = 0x00010000, + SkipRemovedArguments = 0x00020000, + IncludeDefaultExpression = 0x00040000, + NoReturnStatement = 0x00080000, + NoBlockedSlot = 0x00100000, + + SuperCall = 0x00200000, + + GlobalRefJObject = 0x00100000, + + SkipDefaultValues = 0x00400000, + + WriteSelf = 0x00800000, + ExcludeMethodConst = 0x01000000, + + ForceValueType = ExcludeReference | ExcludeConst + }; + + Generator(); + virtual ~Generator(); + + void setBuilder(AbstractMetaBuilder* builder); + /** + * Returns the generator name. + */ + virtual const char* name() const + { + return ""; + } + + virtual QMap options() const + { + return QMap(); + } + + /** + * The Generator identifies itself through this method, + * as a CodeType generator or a DocumentationType generator. + * If not reimplemented this method the Generator will be + * of CodeType. + * /return a GeneratorType value identifying the kind of + * generator this is + */ + virtual GeneratorType type() const + { + return CodeType; + } + + /// Returns the classes used to generate the binding code. + AbstractMetaClassList classes() const + { + return m_classes; + } + + AbstractMetaFunctionList globalFunctions() const + { + return m_globalFunctions; + } + + AbstractMetaEnumList globalEnums() const + { + return m_globalEnums; + } + + QList primitiveTypes() const + { + return m_primitiveTypes; + } + + QList containerTypes() const + { + return m_containerTypes; + } + + /// Returns the output directory + QString outputDirectory() const + { + return m_outDir; + } + + /// Set the output directory + void setOutputDirectory(const QString &outDir) + { + m_outDir = outDir; + } + + virtual bool prepareGeneration(const QMap& args) = 0; + /** + * Start the code generation, be sure to call setClasses before callign this method. + * For each class it creates a QTextStream, call the write method with the current + * class and the associated text stream, then write the text stream contents if needed. + * \see #write + */ + void generate(); + + /// Returns the number of generated items + int numGenerated() + { + return m_numGenerated; + } + + /// Returns the number of generated items written + int numGeneratedAndWritten() + { + return m_numGeneratedWritten; + } + + /// Returns true if the generator should generate any code for the AbstractMetaClass + virtual bool shouldGenerate(const AbstractMetaClass *) const; + + /// Returns the subdirectory used to write the binding code of an AbstractMetaClass. + virtual QString subDirectoryForClass(const AbstractMetaClass* clazz) const = 0; + + /** + * Translate metatypes to binding source format. + * \param metatype a pointer to metatype + * \param context the current meta class + * \param option some extra options + * \return the metatype translated to binding source format + */ + virtual QString translateType(const AbstractMetaType *metatype, + const AbstractMetaClass *context, + int option = NoOption) const = 0; + + /** + * Function used to write the fucntion arguments on the class buffer. + * \param s the class output buffer + * \param metafunction the pointer to metafunction information + * \param count the number of function arguments + * \param options some extra options used during the parser + */ + virtual void writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction *metafunction, + uint options = 0) const = 0; + + virtual void writeArgumentNames(QTextStream &s, + const AbstractMetaFunction *metafunction, + uint options = 0) const = 0; + + void replaceTemplateVariables(QString &code, const AbstractMetaFunction *func); + + bool hasDefaultConstructor(const AbstractMetaType *type); + + // QtScript + QSet qtMetaTypeDeclaredTypeNames() const + { + return m_qmetatypeDeclaredTypenames; + } + + /** + * Returns the license comment to be prepended to each source file generated. + */ + QString licenseComment() + { + return m_licenseComment; + } + + /** + * Sets the license comment to be prepended to each source file generated. + */ + void setLicenseComment(const QString &licenseComment) + { + m_licenseComment = licenseComment; + } + + /** + * Returns the package name. + */ + QString packageName() + { + return m_packageName; + } + + /** + * Sets the package name. + */ + void setPackageName(const QString &packageName) + { + m_packageName = packageName; + } + + /** + * Retrieves the name of the currently processed module. While package name + * is a complete package idetification, e.g. 'PySide.QtCore', a module name + * represents the last part of the package, e.g. 'QtCore'. + * If the target language separates the modules with characters other than + * dots ('.') the generator subclass must overload this method. + * /return a string representing the last part of a package name + */ + virtual QString moduleName() + { + return QString(m_packageName).remove(0, m_packageName.lastIndexOf('.') + 1); + } + +protected: + QString m_packageName; + + /** + * Returns the file name used to write the binding code of an AbstractMetaClass. + * /param metaClass the AbstractMetaClass for which the file name must be + * returned + * /return the file name used to write the binding code for the class + */ + virtual QString fileNameForClass(const AbstractMetaClass* metaClass) const = 0; + + /** + * Returns the subdirectory path for a given package + * (aka module, aka library) name. + * If the target language separates the package modules with characters other + * than dots ('.') the generator subclass must overload this method. + * /param packageName complete package name for which to return the subdirectory path + * or nothing the use the name of the currently processed package + * /return a string representing the subdirectory path for the given package + */ + virtual QString subDirectoryForPackage(QString packageName = QString()) const + { + if (packageName.isEmpty()) + packageName = m_packageName; + return QString(packageName).replace(".", QDir::separator()); + } + + /** + * Write the bindding code for an AbstractMetaClass. + * This is called by the default implementation of generate method. + * \param s text stream to write the generated output + * \param metaClass the class that should be generated + */ + virtual void generateClass(QTextStream& s, const AbstractMetaClass* metaClass) = 0; + virtual void finishGeneration() = 0; + + void verifyDirectoryFor(const QFile &file); + + int m_numGenerated; + int m_numGeneratedWritten; + +private: + AbstractMetaClassList m_classes; + AbstractMetaFunctionList m_globalFunctions; + AbstractMetaEnumList m_globalEnums; + QString m_outDir; + + QList m_primitiveTypes; + QList m_containerTypes; + + // QtScript + QSet m_qmetatypeDeclaredTypenames; + + // License comment + QString m_licenseComment; +}; + +/** +* Utility class to store the identation level, use it in a QTextStream. +*/ +class Indentor +{ +public: + Indentor(): + indent(0) {} + int indent; +}; + +/** +* Class that use the RAII idiom to set and unset the identation level. +*/ +class Indentation +{ +public: + Indentation(Indentor &indentor) : indentor(indentor) + { + indentor.indent++; + } + ~Indentation() + { + indentor.indent--; + } + +private: + Indentor &indentor; +}; + +inline QTextStream &operator <<(QTextStream &s, const Indentor &indentor) +{ + for (int i = 0; i < indentor.indent; ++i) + s << " "; + return s; +} + +#endif // GENERATOR_H + diff --git a/generator.qrc b/generator.qrc new file mode 100644 index 000000000..2d82b37cb --- /dev/null +++ b/generator.qrc @@ -0,0 +1,5 @@ + + +parser/rpp/pp-qt-configuration + + diff --git a/merge.xsl b/merge.xsl new file mode 100644 index 000000000..d0b7eafa5 --- /dev/null +++ b/merge.xsl @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parser/ast.cpp b/parser/ast.cpp new file mode 100644 index 000000000..ff5b7dfc8 --- /dev/null +++ b/parser/ast.cpp @@ -0,0 +1,33 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "ast.h" +#include "lexer.h" + +QString AST::toString(TokenStream *stream) const +{ + const Token &tk = stream->token((int) start_token); + const Token &end_tk = stream->token((int) end_token); + return QString::fromLatin1(tk.text + tk.position, end_tk.position - tk.position); +} diff --git a/parser/ast.h b/parser/ast.h new file mode 100644 index 000000000..aa98ee4db --- /dev/null +++ b/parser/ast.h @@ -0,0 +1,879 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef AST_H +#define AST_H + +#include "smallobject.h" +#include "list.h" + +class QString; + +#define DECLARE_AST_NODE(k) \ + enum { __node_kind = Kind_##k }; + +class TokenStream; + +struct AccessSpecifierAST; +struct AsmDefinitionAST; +struct BaseClauseAST; +struct BaseSpecifierAST; +struct BinaryExpressionAST; +struct CastExpressionAST; +struct ClassMemberAccessAST; +struct ClassSpecifierAST; +struct CompoundStatementAST; +struct ConditionAST; +struct ConditionalExpressionAST; +struct CppCastExpressionAST; +struct CtorInitializerAST; +struct DeclarationAST; +struct DeclarationStatementAST; +struct DeclaratorAST; +struct DeleteExpressionAST; +struct DoStatementAST; +struct ElaboratedTypeSpecifierAST; +struct EnumSpecifierAST; +struct EnumeratorAST; +struct ExceptionSpecificationAST; +struct ExpressionAST; +struct ExpressionOrDeclarationStatementAST; +struct ExpressionStatementAST; +struct ForStatementAST; +struct FunctionCallAST; +struct FunctionDefinitionAST; +struct IfStatementAST; +struct IncrDecrExpressionAST; +struct InitDeclaratorAST; +struct InitializerAST; +struct InitializerClauseAST; +struct LabeledStatementAST; +struct LinkageBodyAST; +struct LinkageSpecificationAST; +struct MemInitializerAST; +struct NameAST; +struct NamespaceAST; +struct NamespaceAliasDefinitionAST; +struct NewDeclaratorAST; +struct NewExpressionAST; +struct NewInitializerAST; +struct NewTypeIdAST; +struct OperatorAST; +struct OperatorFunctionIdAST; +struct ParameterDeclarationAST; +struct ParameterDeclarationClauseAST; +struct PostfixExpressionAST; +struct PrimaryExpressionAST; +struct PtrOperatorAST; +struct PtrToMemberAST; +struct ReturnStatementAST; +struct SimpleDeclarationAST; +struct SimpleTypeSpecifierAST; +struct SizeofExpressionAST; +struct StatementAST; +struct StringLiteralAST; +struct SubscriptExpressionAST; +struct SwitchStatementAST; +struct TemplateArgumentAST; +struct TemplateDeclarationAST; +struct TemplateParameterAST; +struct ThrowExpressionAST; +struct TranslationUnitAST; +struct TryBlockStatementAST; +struct TypeIdAST; +struct TypeIdentificationAST; +struct TypeParameterAST; +struct TypeSpecifierAST; +struct TypedefAST; +struct UnaryExpressionAST; +struct UnqualifiedNameAST; +struct UsingAST; +struct UsingDirectiveAST; +struct WhileStatementAST; +struct WinDeclSpecAST; +struct QPropertyAST; +struct QEnumsAST; + +struct AST +{ + enum NODE_KIND { + Kind_UNKNOWN = 0, + + Kind_AccessSpecifier, + Kind_AsmDefinition, + Kind_BaseClause, + Kind_BaseSpecifier, + Kind_BinaryExpression, + Kind_CastExpression, + Kind_ClassMemberAccess, + Kind_ClassSpecifier, + Kind_CompoundStatement, + Kind_Condition, + Kind_ConditionalExpression, + Kind_CppCastExpression, + Kind_CtorInitializer, + Kind_DeclarationStatement, + Kind_Declarator, + Kind_DeleteExpression, + Kind_DoStatement, + Kind_ElaboratedTypeSpecifier, + Kind_EnumSpecifier, + Kind_Enumerator, + Kind_ExceptionSpecification, + Kind_ExpressionOrDeclarationStatement, + Kind_ExpressionStatement, + Kind_ForStatement, + Kind_FunctionCall, + Kind_FunctionDefinition, + Kind_IfStatement, + Kind_IncrDecrExpression, + Kind_InitDeclarator, + Kind_Initializer, + Kind_InitializerClause, + Kind_LabeledStatement, + Kind_LinkageBody, + Kind_LinkageSpecification, + Kind_MemInitializer, + Kind_Name, + Kind_Namespace, + Kind_NamespaceAliasDefinition, + Kind_NewDeclarator, + Kind_NewExpression, + Kind_NewInitializer, + Kind_NewTypeId, + Kind_Operator, + Kind_OperatorFunctionId, + Kind_ParameterDeclaration, + Kind_ParameterDeclarationClause, + Kind_PostfixExpression, + Kind_PrimaryExpression, + Kind_PtrOperator, + Kind_PtrToMember, + Kind_ReturnStatement, + Kind_SimpleDeclaration, + Kind_SimpleTypeSpecifier, + Kind_SizeofExpression, + Kind_StringLiteral, + Kind_SubscriptExpression, + Kind_SwitchStatement, + Kind_TemplateArgument, + Kind_TemplateDeclaration, + Kind_TemplateParameter, + Kind_ThrowExpression, + Kind_TranslationUnit, + Kind_TryBlockStatement, + Kind_TypeId, + Kind_TypeIdentification, + Kind_TypeParameter, + Kind_Typedef, + Kind_UnaryExpression, + Kind_UnqualifiedName, + Kind_Using, + Kind_UsingDirective, + Kind_WhileStatement, + Kind_WinDeclSpec, + Kind_QPropertyAST, + Kind_ForwardDeclarationSpecifier, + Kind_QEnumsAST, + + NODE_KIND_COUNT + }; + + QString toString(TokenStream *stream) const; + + int kind; + + std::size_t start_token; + std::size_t end_token; +}; + +struct TypeSpecifierAST: public AST +{ + const ListNode *cv; +}; + +struct StatementAST: public AST +{ +}; + +struct ExpressionAST: public AST +{ +}; + +struct DeclarationAST: public AST +{ +}; + +struct AccessSpecifierAST: public DeclarationAST +{ + DECLARE_AST_NODE(AccessSpecifier) + + const ListNode *specs; +}; + +struct AsmDefinitionAST: public DeclarationAST +{ + DECLARE_AST_NODE(AsmDefinition) + + const ListNode *cv; +}; + +struct BaseClauseAST: public AST +{ // ### kill me + DECLARE_AST_NODE(BaseClause) + + const ListNode *base_specifiers; +}; + +struct BaseSpecifierAST: public AST +{ + DECLARE_AST_NODE(BaseSpecifier) + + std::size_t virt; + std::size_t access_specifier; + NameAST *name; +}; + +struct BinaryExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(BinaryExpression) + + std::size_t op; + ExpressionAST *left_expression; + ExpressionAST *right_expression; +}; + +struct CastExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(CastExpression) + + TypeIdAST *type_id; + ExpressionAST *expression; +}; + +struct ClassMemberAccessAST: public ExpressionAST +{ + DECLARE_AST_NODE(ClassMemberAccess) + + std::size_t op; + NameAST *name; +}; + +struct ClassSpecifierAST: public TypeSpecifierAST +{ + DECLARE_AST_NODE(ClassSpecifier) + + WinDeclSpecAST *win_decl_specifiers; + std::size_t class_key; + NameAST *name; + BaseClauseAST *base_clause; + const ListNode *member_specs; +}; + +struct ForwardDeclarationSpecifierAST: public TypeSpecifierAST +{ + DECLARE_AST_NODE(ForwardDeclarationSpecifier) + + std::size_t class_key; + NameAST *name; + BaseClauseAST *base_clause; +}; + +struct CompoundStatementAST: public StatementAST +{ + DECLARE_AST_NODE(CompoundStatement) + + const ListNode *statements; +}; + +struct ConditionAST: public AST +{ + DECLARE_AST_NODE(Condition) + + TypeSpecifierAST *type_specifier; + DeclaratorAST *declarator; + ExpressionAST *expression; +}; + +struct ConditionalExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(ConditionalExpression) + + ExpressionAST *condition; + ExpressionAST *left_expression; + ExpressionAST *right_expression; +}; + +struct CppCastExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(CppCastExpression) + + std::size_t op; + TypeIdAST *type_id; + ExpressionAST *expression; + const ListNode *sub_expressions; +}; + +struct CtorInitializerAST: public AST +{ + DECLARE_AST_NODE(CtorInitializer) + + std::size_t colon; + const ListNode *member_initializers; +}; + +struct DeclarationStatementAST: public StatementAST +{ + DECLARE_AST_NODE(DeclarationStatement) + + DeclarationAST *declaration; +}; + +struct DeclaratorAST: public AST +{ + DECLARE_AST_NODE(Declarator) + + const ListNode *ptr_ops; + DeclaratorAST *sub_declarator; + NameAST *id; + ExpressionAST *bit_expression; + const ListNode *array_dimensions; + ParameterDeclarationClauseAST *parameter_declaration_clause; + const ListNode *fun_cv; + ExceptionSpecificationAST *exception_spec; +}; + +struct DeleteExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(DeleteExpression) + + std::size_t scope_token; + std::size_t delete_token; + std::size_t lbracket_token; + std::size_t rbracket_token; + ExpressionAST *expression; +}; + +struct DoStatementAST: public StatementAST +{ + DECLARE_AST_NODE(DoStatement) + + StatementAST *statement; + ExpressionAST *expression; +}; + +struct ElaboratedTypeSpecifierAST: public TypeSpecifierAST +{ + DECLARE_AST_NODE(ElaboratedTypeSpecifier) + + std::size_t type; + NameAST *name; +}; + +struct EnumSpecifierAST: public TypeSpecifierAST +{ + DECLARE_AST_NODE(EnumSpecifier) + + NameAST *name; + const ListNode *enumerators; +}; + +struct EnumeratorAST: public AST +{ + DECLARE_AST_NODE(Enumerator) + + std::size_t id; + ExpressionAST *expression; +}; + +struct ExceptionSpecificationAST: public AST +{ + DECLARE_AST_NODE(ExceptionSpecification) + + std::size_t ellipsis; + const ListNode *type_ids; +}; + +struct ExpressionOrDeclarationStatementAST: public StatementAST +{ + DECLARE_AST_NODE(ExpressionOrDeclarationStatement) + + StatementAST *expression; + StatementAST *declaration; +}; + +struct ExpressionStatementAST: public StatementAST +{ + DECLARE_AST_NODE(ExpressionStatement) + + ExpressionAST *expression; +}; + +struct FunctionCallAST: public ExpressionAST +{ + DECLARE_AST_NODE(FunctionCall) + + ExpressionAST *arguments; +}; + +struct FunctionDefinitionAST: public DeclarationAST +{ + DECLARE_AST_NODE(FunctionDefinition) + + const ListNode *storage_specifiers; + const ListNode *function_specifiers; + TypeSpecifierAST *type_specifier; + InitDeclaratorAST *init_declarator; + StatementAST *function_body; + WinDeclSpecAST *win_decl_specifiers; +}; + +struct ForStatementAST: public StatementAST +{ + DECLARE_AST_NODE(ForStatement) + + StatementAST *init_statement; + ConditionAST *condition; + ExpressionAST *expression; + StatementAST *statement; +}; + +struct IfStatementAST: public StatementAST +{ + DECLARE_AST_NODE(IfStatement) + + ConditionAST *condition; + StatementAST *statement; + StatementAST *else_statement; +}; + +struct IncrDecrExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(IncrDecrExpression) + + std::size_t op; +}; + +struct InitDeclaratorAST: public AST +{ + DECLARE_AST_NODE(InitDeclarator) + + DeclaratorAST *declarator; + InitializerAST *initializer; +}; + +struct InitializerAST: public AST +{ + DECLARE_AST_NODE(Initializer) + + InitializerClauseAST *initializer_clause; + ExpressionAST *expression; +}; + +struct InitializerClauseAST: public AST +{ + DECLARE_AST_NODE(InitializerClause) + + ExpressionAST *expression; +}; + +struct LabeledStatementAST: public StatementAST +{ + DECLARE_AST_NODE(LabeledStatement) +}; + +struct LinkageBodyAST: public AST +{ + DECLARE_AST_NODE(LinkageBody) + + const ListNode *declarations; +}; + +struct LinkageSpecificationAST: public DeclarationAST +{ + DECLARE_AST_NODE(LinkageSpecification) + + std::size_t extern_type; + LinkageBodyAST *linkage_body; + DeclarationAST *declaration; +}; + +struct MemInitializerAST: public AST +{ + DECLARE_AST_NODE(MemInitializer) + + NameAST *initializer_id; + ExpressionAST *expression; +}; + +struct NameAST: public AST +{ + DECLARE_AST_NODE(Name) + + bool global; + const ListNode *qualified_names; + UnqualifiedNameAST *unqualified_name; +}; + +struct NamespaceAST: public DeclarationAST +{ + DECLARE_AST_NODE(Namespace) + + std::size_t namespace_name; + LinkageBodyAST *linkage_body; +}; + +struct NamespaceAliasDefinitionAST: public DeclarationAST +{ + DECLARE_AST_NODE(NamespaceAliasDefinition) + + std::size_t namespace_name; + NameAST *alias_name; +}; + +struct NewDeclaratorAST: public AST +{ + DECLARE_AST_NODE(NewDeclarator) + + PtrOperatorAST *ptr_op; + NewDeclaratorAST *sub_declarator; + const ListNode *expressions; +}; + +struct NewExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(NewExpression) + + std::size_t scope_token; + std::size_t new_token; + ExpressionAST *expression; + TypeIdAST *type_id; + NewTypeIdAST *new_type_id; + NewInitializerAST *new_initializer; +}; + +struct NewInitializerAST: public AST +{ + DECLARE_AST_NODE(NewInitializer) + + ExpressionAST *expression; +}; + +struct NewTypeIdAST: public AST +{ + DECLARE_AST_NODE(NewTypeId) + + TypeSpecifierAST *type_specifier; + NewInitializerAST *new_initializer; + NewDeclaratorAST *new_declarator; +}; + +struct OperatorAST: public AST +{ + DECLARE_AST_NODE(Operator) + + std::size_t op; + std::size_t open; + std::size_t close; +}; + +struct OperatorFunctionIdAST: public AST +{ + DECLARE_AST_NODE(OperatorFunctionId) + + OperatorAST *op; + TypeSpecifierAST *type_specifier; + const ListNode *ptr_ops; +}; + +struct ParameterDeclarationAST: public AST +{ + DECLARE_AST_NODE(ParameterDeclaration) + + TypeSpecifierAST *type_specifier; + DeclaratorAST *declarator; + ExpressionAST *expression; +}; + +struct ParameterDeclarationClauseAST: public AST +{ + DECLARE_AST_NODE(ParameterDeclarationClause) + + const ListNode *parameter_declarations; + std::size_t ellipsis; +}; + +struct PostfixExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(PostfixExpression) + + TypeSpecifierAST *type_specifier; + ExpressionAST *expression; + const ListNode *sub_expressions; +}; + +struct PrimaryExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(PrimaryExpression) + + StringLiteralAST *literal; + std::size_t token; + StatementAST *expression_statement; + ExpressionAST *sub_expression; + NameAST *name; +}; + +struct PtrOperatorAST: public AST +{ + DECLARE_AST_NODE(PtrOperator) + + const ListNode *cv; + std::size_t op; + PtrToMemberAST *mem_ptr; +}; + +struct PtrToMemberAST: public AST +{ + DECLARE_AST_NODE(PtrToMember) +}; + +struct ReturnStatementAST: public StatementAST +{ + DECLARE_AST_NODE(ReturnStatement) + + ExpressionAST *expression; +}; + +struct SimpleDeclarationAST: public DeclarationAST +{ + DECLARE_AST_NODE(SimpleDeclaration) + + const ListNode *storage_specifiers; + const ListNode *function_specifiers; + TypeSpecifierAST *type_specifier; + const ListNode *init_declarators; + WinDeclSpecAST *win_decl_specifiers; +}; + +struct SimpleTypeSpecifierAST: public TypeSpecifierAST +{ + DECLARE_AST_NODE(SimpleTypeSpecifier) + + const ListNode *integrals; + std::size_t type_of; + TypeIdAST *type_id; + ExpressionAST *expression; + NameAST *name; +}; + +struct SizeofExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(SizeofExpression) + + std::size_t sizeof_token; + TypeIdAST *type_id; + ExpressionAST *expression; +}; + +struct StringLiteralAST: public AST +{ + DECLARE_AST_NODE(StringLiteral) + + const ListNode *literals; +}; + +struct SubscriptExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(SubscriptExpression) + + ExpressionAST *subscript; +}; + +struct SwitchStatementAST: public StatementAST +{ + DECLARE_AST_NODE(SwitchStatement) + + ConditionAST *condition; + StatementAST *statement; +}; + +struct TemplateArgumentAST: public AST +{ + DECLARE_AST_NODE(TemplateArgument) + + TypeIdAST *type_id; + ExpressionAST *expression; +}; + +struct TemplateDeclarationAST: public DeclarationAST +{ + DECLARE_AST_NODE(TemplateDeclaration) + + std::size_t exported; + const ListNode *template_parameters; + DeclarationAST* declaration; +}; + +struct TemplateParameterAST: public AST +{ + DECLARE_AST_NODE(TemplateParameter) + + TypeParameterAST *type_parameter; + ParameterDeclarationAST *parameter_declaration; +}; + +struct ThrowExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(ThrowExpression) + + std::size_t throw_token; + ExpressionAST *expression; +}; + +struct TranslationUnitAST: public AST +{ + DECLARE_AST_NODE(TranslationUnit) + + const ListNode *declarations; +}; + +struct TryBlockStatementAST: public StatementAST +{ + DECLARE_AST_NODE(TryBlockStatement) +}; + +struct TypeIdAST: public AST +{ + DECLARE_AST_NODE(TypeId) + + TypeSpecifierAST *type_specifier; + DeclaratorAST *declarator; +}; + +struct TypeIdentificationAST: public ExpressionAST +{ + DECLARE_AST_NODE(TypeIdentification) + + std::size_t typename_token; + NameAST *name; + ExpressionAST *expression; +}; + +struct TypeParameterAST: public AST +{ + DECLARE_AST_NODE(TypeParameter) + + std::size_t type; + NameAST *name; + TypeIdAST *type_id; + const ListNode *template_parameters; + NameAST *template_name; +}; + +struct TypedefAST: public DeclarationAST +{ + DECLARE_AST_NODE(Typedef) + + TypeSpecifierAST *type_specifier; + const ListNode *init_declarators; +}; + +struct UnaryExpressionAST: public ExpressionAST +{ + DECLARE_AST_NODE(UnaryExpression) + + std::size_t op; + ExpressionAST *expression; +}; + +struct UnqualifiedNameAST: public AST +{ + DECLARE_AST_NODE(UnqualifiedName) + + std::size_t tilde; + std::size_t id; + OperatorFunctionIdAST *operator_id; + const ListNode *template_arguments; +}; + +struct UsingAST: public DeclarationAST +{ + DECLARE_AST_NODE(Using) + + std::size_t type_name; + NameAST *name; +}; + +struct UsingDirectiveAST: public DeclarationAST +{ + DECLARE_AST_NODE(UsingDirective) + + NameAST *name; +}; + +struct WhileStatementAST: public StatementAST +{ + DECLARE_AST_NODE(WhileStatement) + + ConditionAST *condition; + StatementAST *statement; +}; + +struct WinDeclSpecAST: public AST +{ + DECLARE_AST_NODE(WinDeclSpec) + + std::size_t specifier; + std::size_t modifier; +}; + +struct QPropertyAST : public DeclarationAST +{ + DECLARE_AST_NODE(QPropertyAST) +}; + +struct QEnumsAST : public DeclarationAST +{ + DECLARE_AST_NODE(QEnumsAST) +}; + +template +_Tp *CreateNode(pool *memory_pool) +{ + _Tp *node = reinterpret_cast<_Tp*>(memory_pool->allocate(sizeof(_Tp))); + node->kind = _Tp::__node_kind; + return node; +} + +template +_Tp ast_cast(AST *item) +{ + if (item && static_cast<_Tp>(0)->__node_kind == item->kind) + return static_cast<_Tp>(item); + + return 0; +} + +#endif // AST_H diff --git a/parser/binder.cpp b/parser/binder.cpp new file mode 100644 index 000000000..8b6c4c024 --- /dev/null +++ b/parser/binder.cpp @@ -0,0 +1,852 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "binder.h" +#include "lexer.h" +#include "control.h" +#include "symbol.h" +#include "codemodel_finder.h" +#include "class_compiler.h" +#include "compiler_utils.h" +#include "tokens.h" +#include "dumptree.h" + +#include +#include + +Binder::Binder(CodeModel *__model, LocationManager &__location, Control *__control) + : _M_model(__model), + _M_location(__location), + _M_token_stream(&_M_location.token_stream), + _M_control(__control), + _M_current_function_type(CodeModel::Normal), + type_cc(this), + name_cc(this), + decl_cc(this) +{ + _M_qualified_types["char"] = QString(); + _M_qualified_types["double"] = QString(); + _M_qualified_types["float"] = QString(); + _M_qualified_types["int"] = QString(); + _M_qualified_types["long"] = QString(); + _M_qualified_types["short"] = QString(); + _M_qualified_types["void"] = QString(); +} + +Binder::~Binder() +{ +} + +FileModelItem Binder::run(AST *node) +{ + FileModelItem old = _M_current_file; + _M_current_access = CodeModel::Public; + + _M_current_file = model()->create(); + updateItemPosition(_M_current_file->toItem(), node); + visit(node); + FileModelItem result = _M_current_file; + + _M_current_file = old; // restore + + return result; +} + +ScopeModelItem Binder::currentScope() +{ + if (_M_current_class) + return model_static_cast(_M_current_class); + else if (_M_current_namespace) + return model_static_cast(_M_current_namespace); + + return model_static_cast(_M_current_file); +} + +TemplateParameterList Binder::changeTemplateParameters(TemplateParameterList templateParameters) +{ + TemplateParameterList old = _M_current_template_parameters; + _M_current_template_parameters = templateParameters; + return old; +} + +CodeModel::FunctionType Binder::changeCurrentFunctionType(CodeModel::FunctionType functionType) +{ + CodeModel::FunctionType old = _M_current_function_type; + _M_current_function_type = functionType; + return old; +} + +CodeModel::AccessPolicy Binder::changeCurrentAccess(CodeModel::AccessPolicy accessPolicy) +{ + CodeModel::AccessPolicy old = _M_current_access; + _M_current_access = accessPolicy; + return old; +} + +NamespaceModelItem Binder::changeCurrentNamespace(NamespaceModelItem item) +{ + NamespaceModelItem old = _M_current_namespace; + _M_current_namespace = item; + return old; +} + +ClassModelItem Binder::changeCurrentClass(ClassModelItem item) +{ + ClassModelItem old = _M_current_class; + _M_current_class = item; + return old; +} + +FunctionDefinitionModelItem Binder::changeCurrentFunction(FunctionDefinitionModelItem item) +{ + FunctionDefinitionModelItem old = _M_current_function; + _M_current_function = item; + return old; +} + +int Binder::decode_token(std::size_t index) const +{ + return _M_token_stream->kind(index); +} + +CodeModel::AccessPolicy Binder::decode_access_policy(std::size_t index) const +{ + switch (decode_token(index)) { + case Token_class: + return CodeModel::Private; + + case Token_struct: + case Token_union: + return CodeModel::Public; + + default: + return CodeModel::Public; + } +} + +CodeModel::ClassType Binder::decode_class_type(std::size_t index) const +{ + switch (decode_token(index)) { + case Token_class: + return CodeModel::Class; + case Token_struct: + return CodeModel::Struct; + case Token_union: + return CodeModel::Union; + default: + std::cerr << "** WARNING unrecognized class type" << std::endl; + } + return CodeModel::Class; +} + +const NameSymbol *Binder::decode_symbol(std::size_t index) const +{ + return _M_token_stream->symbol(index); +} + +void Binder::visitAccessSpecifier(AccessSpecifierAST *node) +{ + const ListNode *it = node->specs; + if (!it) + return; + + it = it->toFront(); + const ListNode *end = it; + + do { + switch (decode_token(it->element)) { + default: + break; + + case Token_public: + changeCurrentAccess(CodeModel::Public); + changeCurrentFunctionType(CodeModel::Normal); + break; + case Token_protected: + changeCurrentAccess(CodeModel::Protected); + changeCurrentFunctionType(CodeModel::Normal); + break; + case Token_private: + changeCurrentAccess(CodeModel::Private); + changeCurrentFunctionType(CodeModel::Normal); + break; + case Token_signals: + changeCurrentAccess(CodeModel::Protected); + changeCurrentFunctionType(CodeModel::Signal); + break; + case Token_slots: + changeCurrentFunctionType(CodeModel::Slot); + break; + } + it = it->next; + } while (it != end); +} + +void Binder::visitSimpleDeclaration(SimpleDeclarationAST *node) +{ + visit(node->type_specifier); + + if (const ListNode *it = node->init_declarators) { + it = it->toFront(); + const ListNode *end = it; + do { + InitDeclaratorAST *init_declarator = it->element; + declare_symbol(node, init_declarator); + it = it->next; + } while (it != end); + } +} + +void Binder::declare_symbol(SimpleDeclarationAST *node, InitDeclaratorAST *init_declarator) +{ + DeclaratorAST *declarator = init_declarator->declarator; + + while (declarator && declarator->sub_declarator) + declarator = declarator->sub_declarator; + + NameAST *id = declarator->id; + if (!declarator->id) { + std::cerr << "** WARNING expected a declarator id" << std::endl; + return; + } + + CodeModelFinder finder(model(), this); + ScopeModelItem symbolScope = finder.resolveScope(id, currentScope()); + if (!symbolScope) { + name_cc.run(id); + std::cerr << "** WARNING scope not found for symbol:" + << qPrintable(name_cc.name()) << std::endl; + return; + } + + decl_cc.run(declarator); + + if (decl_cc.isFunction()) { + name_cc.run(id->unqualified_name); + + FunctionModelItem fun = model()->create(); + updateItemPosition(fun->toItem(), node); + fun->setAccessPolicy(_M_current_access); + fun->setFunctionType(_M_current_function_type); + fun->setName(name_cc.name()); + fun->setAbstract(init_declarator->initializer != 0); + fun->setConstant(declarator->fun_cv != 0); + fun->setTemplateParameters(_M_current_template_parameters); + applyStorageSpecifiers(node->storage_specifiers, model_static_cast(fun)); + applyFunctionSpecifiers(node->function_specifiers, fun); + + // build the type + TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier, + declarator, + this); + + fun->setType(qualifyType(typeInfo, symbolScope->qualifiedName())); + + + fun->setVariadics(decl_cc.isVariadics()); + + // ... and the signature + foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) { + ArgumentModelItem arg = model()->create(); + arg->setType(qualifyType(p.type, _M_context)); + arg->setName(p.name); + arg->setDefaultValue(p.defaultValue); + if (p.defaultValue) + arg->setDefaultValueExpression(p.defaultValueExpression); + fun->addArgument(arg); + } + + fun->setScope(symbolScope->qualifiedName()); + symbolScope->addFunction(fun); + } else { + VariableModelItem var = model()->create(); + updateItemPosition(var->toItem(), node); + var->setTemplateParameters(_M_current_template_parameters); + var->setAccessPolicy(_M_current_access); + name_cc.run(id->unqualified_name); + var->setName(name_cc.name()); + TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier, + declarator, + this); + if (declarator != init_declarator->declarator + && init_declarator->declarator->parameter_declaration_clause) { + typeInfo.setFunctionPointer(true); + decl_cc.run(init_declarator->declarator); + foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) + typeInfo.addArgument(p.type); + } + + var->setType(qualifyType(typeInfo, _M_context)); + applyStorageSpecifiers(node->storage_specifiers, model_static_cast(var)); + + var->setScope(symbolScope->qualifiedName()); + symbolScope->addVariable(var); + } +} + +void Binder::visitFunctionDefinition(FunctionDefinitionAST *node) +{ + Q_ASSERT(node->init_declarator); + + ScopeModelItem scope = currentScope(); + + InitDeclaratorAST *init_declarator = node->init_declarator; + DeclaratorAST *declarator = init_declarator->declarator; + + // in the case of "void (func)()" or "void ((func))()" we need to + // skip to the inner most. This is in line with how the declarator + // node is generated in 'parser.cpp' + while (declarator && declarator->sub_declarator) + declarator = declarator->sub_declarator; + Q_ASSERT(declarator->id); + + CodeModelFinder finder(model(), this); + + ScopeModelItem functionScope = finder.resolveScope(declarator->id, scope); + if (!functionScope) { + name_cc.run(declarator->id); + std::cerr << "** WARNING scope not found for function definition:" + << qPrintable(name_cc.name()) << std::endl + << "\tdefinition *ignored*" + << std::endl; + return; + } + + decl_cc.run(declarator); + + Q_ASSERT(!decl_cc.id().isEmpty()); + + FunctionDefinitionModelItem + old = changeCurrentFunction(_M_model->create()); + _M_current_function->setScope(functionScope->qualifiedName()); + updateItemPosition(_M_current_function->toItem(), node); + + Q_ASSERT(declarator->id->unqualified_name); + name_cc.run(declarator->id->unqualified_name); + QString unqualified_name = name_cc.name(); + + _M_current_function->setName(unqualified_name); + TypeInfo tmp_type = CompilerUtils::typeDescription(node->type_specifier, + declarator, this); + + _M_current_function->setType(qualifyType(tmp_type, _M_context)); + _M_current_function->setAccessPolicy(_M_current_access); + _M_current_function->setFunctionType(_M_current_function_type); + _M_current_function->setConstant(declarator->fun_cv); + _M_current_function->setTemplateParameters(_M_current_template_parameters); + + applyStorageSpecifiers(node->storage_specifiers, + model_static_cast(_M_current_function)); + applyFunctionSpecifiers(node->function_specifiers, + model_static_cast(_M_current_function)); + + _M_current_function->setVariadics(decl_cc.isVariadics()); + + foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) { + ArgumentModelItem arg = model()->create(); + arg->setType(qualifyType(p.type, functionScope->qualifiedName())); + arg->setName(p.name); + arg->setDefaultValue(p.defaultValue); + if (p.defaultValue) + arg->setDefaultValueExpression(p.defaultValueExpression); + _M_current_function->addArgument(arg); + } + + functionScope->addFunctionDefinition(_M_current_function); + + FunctionModelItem prototype = model_static_cast(_M_current_function); + FunctionModelItem declared = functionScope->declaredFunction(prototype); + + // try to find a function declaration for this definition.. + if (!declared) { + functionScope->addFunction(prototype); + } else { + applyFunctionSpecifiers(node->function_specifiers, declared); + + // fix the function type and the access policy + _M_current_function->setAccessPolicy(declared->accessPolicy()); + _M_current_function->setFunctionType(declared->functionType()); + } + + changeCurrentFunction(old); +} + +void Binder::visitTemplateDeclaration(TemplateDeclarationAST *node) +{ + const ListNode *it = node->template_parameters; + if (!it) { + // QtScript: we want to visit the declaration still, so that + // e.g. QMetaTypeId is added to the code model + visit(node->declaration); + return; + } + + TemplateParameterList savedTemplateParameters = changeTemplateParameters(TemplateParameterList()); + + it = it->toFront(); + const ListNode *end = it; + + TemplateParameterList templateParameters; + do { + TemplateParameterAST *parameter = it->element; + TypeParameterAST *type_parameter = parameter->type_parameter; + + NameAST *name; + if (!type_parameter) { + // A hacky hack to work around missing support for parameter declarations in + // templates. We just need the to get the name of the variable, since we + // aren't actually compiling these anyway. We are still not supporting much + // more, but we are refusing to fail for a few more declarations + if (!parameter->parameter_declaration || + !parameter->parameter_declaration->declarator || + !parameter->parameter_declaration->declarator->id) { + + /*std::cerr << "** WARNING template declaration not supported ``"; + Token const &tk = _M_token_stream->token ((int) node->start_token); + Token const &end_tk = _M_token_stream->token ((int) node->declaration->start_token); + + std::cerr << std::string (&tk.text[tk.position], (end_tk.position) - tk.position) << "''" + << std::endl << std::endl;*/ + + changeTemplateParameters(savedTemplateParameters); + return; + + } + + name = parameter->parameter_declaration->declarator->id; + } else { + int tk = decode_token(type_parameter->type); + if (tk != Token_typename && tk != Token_class) { + /*std::cerr << "** WARNING template declaration not supported ``"; + Token const &tk = _M_token_stream->token ((int) node->start_token); + Token const &end_tk = _M_token_stream->token ((int) node->declaration->start_token); + + std::cerr << std::string (&tk.text[tk.position], (end_tk.position) - tk.position) << "''" + << std::endl << std::endl;*/ + + changeTemplateParameters(savedTemplateParameters); + return; + } + assert(tk == Token_typename || tk == Token_class); + + name = type_parameter->name; + } + + TemplateParameterModelItem p = model()->create(); + name_cc.run(name); + p->setName(name_cc.name()); + + _M_current_template_parameters.append(p); + it = it->next; + } while (it != end); + + visit(node->declaration); + + changeTemplateParameters(savedTemplateParameters); +} + +void Binder::visitTypedef(TypedefAST *node) +{ + const ListNode *it = node->init_declarators; + if (!it) + return; + + it = it->toFront(); + const ListNode *end = it; + + do { + InitDeclaratorAST *init_declarator = it->element; + it = it->next; + + Q_ASSERT(init_declarator->declarator); + + // the name + decl_cc.run(init_declarator->declarator); + QString alias_name = decl_cc.id(); + + if (alias_name.isEmpty()) { + std::cerr << "** WARNING anonymous typedef not supported! ``"; + Token const &tk = _M_token_stream->token((int) node->start_token); + Token const &end_tk = _M_token_stream->token((int) node->end_token); + + std::cerr << std::string(&tk.text[tk.position], end_tk.position - tk.position) << "''" + << std::endl << std::endl; + continue; + } + + // build the type + TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier, + init_declarator->declarator, + this); + DeclaratorAST *decl = init_declarator->declarator; + while (decl && decl->sub_declarator) + decl = decl->sub_declarator; + + if (decl != init_declarator->declarator + && init_declarator->declarator->parameter_declaration_clause) { + typeInfo.setFunctionPointer(true); + decl_cc.run(init_declarator->declarator); + foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) + typeInfo.addArgument(p.type); + } + + ScopeModelItem scope = currentScope(); + DeclaratorAST *declarator = init_declarator->declarator; + CodeModelFinder finder(model(), this); + ScopeModelItem typedefScope = finder.resolveScope(declarator->id, scope); + + TypeAliasModelItem typeAlias = model()->create (); + updateItemPosition(typeAlias->toItem(), node); + typeAlias->setName(alias_name); + typeAlias->setType(qualifyType(typeInfo, currentScope()->qualifiedName())); + typeAlias->setScope(typedefScope->qualifiedName()); + _M_qualified_types[typeAlias->qualifiedName().join(".")] = QString(); + currentScope()->addTypeAlias(typeAlias); + } while (it != end); +} + +void Binder::visitNamespace(NamespaceAST *node) +{ + bool anonymous = (node->namespace_name == 0); + + ScopeModelItem scope = currentScope(); + + NamespaceModelItem old; + if (!anonymous) { + QString name = decode_symbol(node->namespace_name)->as_string(); + + QStringList qualified_name = scope->qualifiedName(); + qualified_name += name; + NamespaceModelItem ns = + model_safe_cast(_M_model->findItem(qualified_name, + _M_current_file->toItem())); + if (!ns) { + ns = _M_model->create(); + updateItemPosition(ns->toItem(), node); + ns->setName(name); + ns->setScope(scope->qualifiedName()); + } + old = changeCurrentNamespace(ns); + + _M_context.append(name); + } + + DefaultVisitor::visitNamespace(node); + + if (!anonymous) { + Q_ASSERT(scope->kind() == _CodeModelItem::Kind_Namespace + || scope->kind() == _CodeModelItem::Kind_File); + + _M_context.removeLast(); + + if (NamespaceModelItem ns = model_static_cast(scope)) + ns->addNamespace(_M_current_namespace); + + changeCurrentNamespace(old); + } +} + +void Binder::visitForwardDeclarationSpecifier(ForwardDeclarationSpecifierAST *node) +{ + name_cc.run(node->name); + if (name_cc.name().isEmpty()) + return; + + ScopeModelItem scope = currentScope(); + _M_qualified_types[(scope->qualifiedName() + name_cc.qualifiedName()).join(".")] = QString(); +} + +void Binder::visitClassSpecifier(ClassSpecifierAST *node) +{ + ClassCompiler class_cc(this); + class_cc.run(node); + + if (class_cc.name().isEmpty()) { + // anonymous not supported + return; + } + + Q_ASSERT(node->name && node->name->unqualified_name); + + ScopeModelItem scope = currentScope(); + + ClassModelItem old = changeCurrentClass(_M_model->create()); + updateItemPosition(_M_current_class->toItem(), node); + _M_current_class->setName(class_cc.name()); + + QStringList baseClasses = class_cc.baseClasses(); + TypeInfo info; + for (int i = 0; i < baseClasses.size(); ++i) { + info.setQualifiedName(baseClasses.at(i).split("::")); + baseClasses[i] = qualifyType(info, scope->qualifiedName()).qualifiedName().join("::"); + } + + _M_current_class->setBaseClasses(baseClasses); + _M_current_class->setClassType(decode_class_type(node->class_key)); + _M_current_class->setTemplateParameters(_M_current_template_parameters); + + if (!_M_current_template_parameters.isEmpty()) { + QString name = _M_current_class->name(); + name += "<"; + for (int i = 0; i < _M_current_template_parameters.size(); ++i) { + if (i > 0) + name += ","; + + name += _M_current_template_parameters.at(i)->name(); + } + + name += ">"; + _M_current_class->setName(name); + } + + CodeModel::AccessPolicy oldAccessPolicy = changeCurrentAccess(decode_access_policy(node->class_key)); + CodeModel::FunctionType oldFunctionType = changeCurrentFunctionType(CodeModel::Normal); + + _M_current_class->setScope(scope->qualifiedName()); + _M_qualified_types[_M_current_class->qualifiedName().join(".")] = QString(); + + scope->addClass(_M_current_class); + + name_cc.run(node->name->unqualified_name); + _M_context.append(name_cc.name()); + visitNodes(this, node->member_specs); + _M_context.removeLast(); + + changeCurrentClass(old); + changeCurrentAccess(oldAccessPolicy); + changeCurrentFunctionType(oldFunctionType); +} + +void Binder::visitLinkageSpecification(LinkageSpecificationAST *node) +{ + DefaultVisitor::visitLinkageSpecification(node); +} + +void Binder::visitUsing(UsingAST *node) +{ + DefaultVisitor::visitUsing(node); +} + +void Binder::visitEnumSpecifier(EnumSpecifierAST *node) +{ + CodeModelFinder finder(model(), this); + ScopeModelItem scope = currentScope(); + ScopeModelItem enumScope = finder.resolveScope(node->name, scope); + + name_cc.run(node->name); + QString name = name_cc.name(); + + if (name.isEmpty()) { + // anonymous enum + QString key = _M_context.join("::"); + int current = ++_M_anonymous_enums[key]; + name += QLatin1String("enum_"); + name += QString::number(current); + } + + _M_current_enum = model()->create(); + _M_current_enum->setAccessPolicy(_M_current_access); + updateItemPosition(_M_current_enum->toItem(), node); + _M_current_enum->setName(name); + _M_current_enum->setScope(enumScope->qualifiedName()); + + _M_qualified_types[_M_current_enum->qualifiedName().join(".")] = QString(); + + enumScope->addEnum(_M_current_enum); + + DefaultVisitor::visitEnumSpecifier(node); + + _M_current_enum = 0; +} + +static QString strip_preprocessor_lines(const QString &name) +{ + QStringList lst = name.split("\n"); + QString s; + for (int i = 0; i < lst.size(); ++i) { + if (!lst.at(i).startsWith('#')) + s += lst.at(i); + } + return s.trimmed(); +} + +void Binder::visitEnumerator(EnumeratorAST *node) +{ + Q_ASSERT(_M_current_enum); + EnumeratorModelItem e = model()->create(); + updateItemPosition(e->toItem(), node); + e->setName(decode_symbol(node->id)->as_string()); + + if (ExpressionAST *expr = node->expression) { + const Token &start_token = _M_token_stream->token((int) expr->start_token); + const Token &end_token = _M_token_stream->token((int) expr->end_token); + + e->setValue(strip_preprocessor_lines(QString::fromUtf8(&start_token.text[start_token.position], + (int)(end_token.position - start_token.position)).trimmed()).remove(' ')); + } + + _M_current_enum->addEnumerator(e); +} + +void Binder::visitUsingDirective(UsingDirectiveAST *node) +{ + DefaultVisitor::visitUsingDirective(node); +} + +void Binder::visitQEnums(QEnumsAST *node) +{ + const Token &start = _M_token_stream->token((int) node->start_token); + const Token &end = _M_token_stream->token((int) node->end_token); + QStringList enum_list = QString::fromLatin1(start.text + start.position, + end.position - start.position).split(' '); + + ScopeModelItem scope = currentScope(); + for (int i = 0; i < enum_list.size(); ++i) + scope->addEnumsDeclaration(enum_list.at(i)); +} + +void Binder::visitQProperty(QPropertyAST *node) +{ + const Token &start = _M_token_stream->token((int) node->start_token); + const Token &end = _M_token_stream->token((int) node->end_token); + QString property = QString::fromLatin1(start.text + start.position, + end.position - start.position); + _M_current_class->addPropertyDeclaration(property); +} + +void Binder::applyStorageSpecifiers(const ListNode *it, MemberModelItem item) +{ + if (!it) + return; + + it = it->toFront(); + const ListNode *end = it; + + do { + switch (decode_token(it->element)) { + default: + break; + + case Token_friend: + item->setFriend(true); + break; + case Token_auto: + item->setAuto(true); + break; + case Token_register: + item->setRegister(true); + break; + case Token_static: + item->setStatic(true); + break; + case Token_extern: + item->setExtern(true); + break; + case Token_mutable: + item->setMutable(true); + break; + } + it = it->next; + } while (it != end); +} + +void Binder::applyFunctionSpecifiers(const ListNode *it, FunctionModelItem item) +{ + if (!it) + return; + + it = it->toFront(); + const ListNode *end = it; + + do { + switch (decode_token(it->element)) { + default: + break; + + case Token_inline: + item->setInline(true); + break; + + case Token_virtual: + item->setVirtual(true); + break; + + case Token_explicit: + item->setExplicit(true); + break; + + case Token_Q_INVOKABLE: + item->setInvokable(true); + break; + } + it = it->next; + } while (it != end); +} + +TypeInfo Binder::qualifyType(const TypeInfo &type, const QStringList &context) const +{ + // ### Potentially improve to use string list in the name table to + if (!context.size()) { + // ### We can assume that this means global namespace for now... + return type; + } else if (_M_qualified_types.contains(type.qualifiedName().join("."))) { + return type; + } else { + QStringList expanded = context; + expanded << type.qualifiedName(); + if (_M_qualified_types.contains(expanded.join("."))) { + TypeInfo modified_type = type; + modified_type.setQualifiedName(expanded); + return modified_type; + } else { + CodeModelItem scope = model()->findItem(context, _M_current_file->toItem()); + + if (ClassModelItem klass = model_dynamic_cast (scope)) { + foreach (QString base, klass->baseClasses()) { + QStringList ctx = context; + ctx.removeLast(); + ctx.append(base); + + TypeInfo qualified = qualifyType(type, ctx); + if (qualified != type) + return qualified; + } + } + + QStringList copy = context; + copy.removeLast(); + return qualifyType(type, copy); + } + } +} + +void Binder::updateItemPosition(CodeModelItem item, AST *node) +{ + QString filename; + int line, column; + + assert(node); + _M_location.positionAt(_M_token_stream->position(node->start_token), &line, &column, &filename); + item->setFileName(filename); +} diff --git a/parser/binder.h b/parser/binder.h new file mode 100644 index 000000000..b4d4da667 --- /dev/null +++ b/parser/binder.h @@ -0,0 +1,125 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef BINDER_H +#define BINDER_H + +#include "default_visitor.h" +#include "codemodel.h" +#include "type_compiler.h" +#include "name_compiler.h" +#include "declarator_compiler.h" + +class TokenStream; +class LocationManager; +class Control; +struct NameSymbol; + +class Binder: protected DefaultVisitor +{ +public: + Binder(CodeModel *__model, LocationManager &__location, Control *__control = 0); + virtual ~Binder(); + + inline TokenStream *tokenStream() const + { + return _M_token_stream; + } + + inline CodeModel *model() const + { + return _M_model; + } + + ScopeModelItem currentScope(); + + FileModelItem run(AST *node); + +// utils + TypeInfo qualifyType(const TypeInfo &type, const QStringList &context) const; + +protected: + virtual void visitAccessSpecifier(AccessSpecifierAST *); + virtual void visitClassSpecifier(ClassSpecifierAST *); + virtual void visitEnumSpecifier(EnumSpecifierAST *); + virtual void visitEnumerator(EnumeratorAST *); + virtual void visitFunctionDefinition(FunctionDefinitionAST *); + virtual void visitLinkageSpecification(LinkageSpecificationAST *); + virtual void visitNamespace(NamespaceAST *); + virtual void visitSimpleDeclaration(SimpleDeclarationAST *); + virtual void visitTemplateDeclaration(TemplateDeclarationAST *); + virtual void visitTypedef(TypedefAST *); + virtual void visitUsing(UsingAST *); + virtual void visitUsingDirective(UsingDirectiveAST *); + virtual void visitQProperty(QPropertyAST *); + virtual void visitForwardDeclarationSpecifier(ForwardDeclarationSpecifierAST *); + virtual void visitQEnums(QEnumsAST *); + +private: + + int decode_token(std::size_t index) const; + const NameSymbol *decode_symbol(std::size_t index) const; + CodeModel::AccessPolicy decode_access_policy(std::size_t index) const; + CodeModel::ClassType decode_class_type(std::size_t index) const; + + CodeModel::FunctionType changeCurrentFunctionType(CodeModel::FunctionType functionType); + CodeModel::AccessPolicy changeCurrentAccess(CodeModel::AccessPolicy accessPolicy); + NamespaceModelItem changeCurrentNamespace(NamespaceModelItem item); + ClassModelItem changeCurrentClass(ClassModelItem item); + FunctionDefinitionModelItem changeCurrentFunction(FunctionDefinitionModelItem item); + TemplateParameterList changeTemplateParameters(TemplateParameterList templateParameters); + + void declare_symbol(SimpleDeclarationAST *node, InitDeclaratorAST *init_declarator); + + void applyStorageSpecifiers(const ListNode *storage_specifiers, MemberModelItem item); + void applyFunctionSpecifiers(const ListNode *it, FunctionModelItem item); + + void updateItemPosition(CodeModelItem item, AST *node); + +private: + CodeModel *_M_model; + LocationManager &_M_location; + TokenStream *_M_token_stream; + Control *_M_control; + + CodeModel::FunctionType _M_current_function_type; + CodeModel::AccessPolicy _M_current_access; + FileModelItem _M_current_file; + NamespaceModelItem _M_current_namespace; + ClassModelItem _M_current_class; + FunctionDefinitionModelItem _M_current_function; + EnumModelItem _M_current_enum; + QStringList _M_context; + TemplateParameterList _M_current_template_parameters; // ### check me + QHash _M_qualified_types; + QHash _M_anonymous_enums; + +protected: + TypeCompiler type_cc; + NameCompiler name_cc; + DeclaratorCompiler decl_cc; +}; + +#endif // BINDER_H diff --git a/parser/class_compiler.cpp b/parser/class_compiler.cpp new file mode 100644 index 000000000..e04ffe109 --- /dev/null +++ b/parser/class_compiler.cpp @@ -0,0 +1,66 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "class_compiler.h" +#include "lexer.h" +#include "binder.h" + +ClassCompiler::ClassCompiler(Binder *binder) + : _M_binder(binder), + _M_token_stream(binder->tokenStream()), + name_cc(_M_binder), + type_cc(_M_binder) +{ +} + +ClassCompiler::~ClassCompiler() +{ +} + +void ClassCompiler::run(ClassSpecifierAST *node) +{ + name_cc.run(node->name); + _M_name = name_cc.name(); + _M_base_classes.clear(); + + visit(node); +} + +void ClassCompiler::visitClassSpecifier(ClassSpecifierAST *node) +{ + visit(node->base_clause); +} + +void ClassCompiler::visitBaseSpecifier(BaseSpecifierAST *node) +{ + name_cc.run(node->name); + QString name = name_cc.name(); + + if (!name.isEmpty()) + _M_base_classes.append(name); +} + + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/class_compiler.h b/parser/class_compiler.h new file mode 100644 index 000000000..69fccf757 --- /dev/null +++ b/parser/class_compiler.h @@ -0,0 +1,72 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef CLASS_COMPILER_H +#define CLASS_COMPILER_H + +#include +#include + +#include +#include +#include + +class TokenStream; +class Binder; + +class ClassCompiler: protected DefaultVisitor +{ +public: + ClassCompiler(Binder *binder); + virtual ~ClassCompiler(); + + inline QString name() const + { + return _M_name; + } + + inline QStringList baseClasses() const + { + return _M_base_classes; + } + + void run(ClassSpecifierAST *node); + +protected: + virtual void visitClassSpecifier(ClassSpecifierAST *node); + virtual void visitBaseSpecifier(BaseSpecifierAST *node); + +private: + Binder *_M_binder; + TokenStream *_M_token_stream; + QString _M_name; + QStringList _M_base_classes; + NameCompiler name_cc; + TypeCompiler type_cc; +}; + +#endif // CLASS_COMPILER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/codemodel.cpp b/parser/codemodel.cpp new file mode 100644 index 000000000..ecc8e3036 --- /dev/null +++ b/parser/codemodel.cpp @@ -0,0 +1,932 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "codemodel.h" + +// --------------------------------------------------------------------------- +CodeModel::CodeModel() + : _M_creation_id(0) +{ + _M_globalNamespace = create(); +} + +CodeModel::~CodeModel() +{ +} + +void CodeModel::wipeout() +{ + _M_globalNamespace = create(); + _M_files.clear(); +} + +FileList CodeModel::files() const +{ + return _M_files.values(); +} + +NamespaceModelItem CodeModel::globalNamespace() const +{ + return _M_globalNamespace; +} + +void CodeModel::addFile(FileModelItem item) +{ + _M_creation_id = 0; // reset the creation id + _M_files.insert(item->name(), item); +} + +void CodeModel::removeFile(FileModelItem item) +{ + QHash::Iterator it = _M_files.find(item->name()); + + if (it != _M_files.end() && it.value() == item) + _M_files.erase(it); +} + +FileModelItem CodeModel::findFile(const QString &name) const +{ + return _M_files.value(name); +} + +QHash CodeModel::fileMap() const +{ + return _M_files; +} + +CodeModelItem CodeModel::findItem(const QStringList &qualifiedName, CodeModelItem scope) const +{ + for (int i = 0; i < qualifiedName.size(); ++i) { + // ### Extend to look for members etc too. + const QString &name = qualifiedName.at(i); + + if (NamespaceModelItem ns = model_dynamic_cast(scope)) { + if (NamespaceModelItem tmp_ns = ns->findNamespace(name)) { + scope = tmp_ns; + continue; + } + } + + if (ScopeModelItem ss = model_dynamic_cast(scope)) { + if (ClassModelItem cs = ss->findClass(name)) { + scope = cs; + } else if (EnumModelItem es = ss->findEnum(name)) { + if (i == qualifiedName.size() - 1) + return es->toItem(); + } else if (TypeAliasModelItem tp = ss->findTypeAlias(name)) { + if (i == qualifiedName.size() - 1) + return tp->toItem(); + } else { + // If we don't find the name in the scope chain we + // need to return an empty item to indicate failure... + return CodeModelItem(); + } + } + } + + return scope; +} + + +// --------------------------------------------------------------------------- +TypeInfo TypeInfo::combine(const TypeInfo &__lhs, const TypeInfo &__rhs) +{ + TypeInfo __result = __lhs; + + __result.setConstant(__result.isConstant() || __rhs.isConstant()); + __result.setVolatile(__result.isVolatile() || __rhs.isVolatile()); + __result.setReference(__result.isReference() || __rhs.isReference()); + __result.setIndirections(__result.indirections() + __rhs.indirections()); + __result.setArrayElements(__result.arrayElements() + __rhs.arrayElements()); + + return __result; +} + +TypeInfo TypeInfo::resolveType(TypeInfo const &__type, CodeModelItem __scope) +{ + CodeModel *__model = __scope->model(); + Q_ASSERT(__model != 0); + + CodeModelItem __item = __model->findItem(__type.qualifiedName(), __scope); + + // Copy the type and replace with the proper qualified name. This + // only makes sence to do if we're actually getting a resolved + // type with a namespace. We only get this if the returned type + // has more than 2 entries in the qualified name... This test + // could be improved by returning if the type was found or not. + TypeInfo otherType(__type); + if (__item && __item->qualifiedName().size() > 1) { + otherType.setQualifiedName(__item->qualifiedName()); + } + + if (TypeAliasModelItem __alias = model_dynamic_cast (__item)) + return resolveType(TypeInfo::combine(__alias->type(), otherType), __scope); + + return otherType; +} + +QString TypeInfo::toString() const +{ + QString tmp; + + tmp += m_qualifiedName.join("::"); + if (isConstant()) + tmp += QLatin1String(" const"); + + if (isVolatile()) + tmp += QLatin1String(" volatile"); + + if (indirections()) + tmp += QString(indirections(), QLatin1Char('*')); + + if (isReference()) + tmp += QLatin1Char('&'); + + if (isFunctionPointer()) { + tmp += QLatin1String(" (*)("); + for (int i = 0; i < m_arguments.count(); ++i) { + if (i != 0) + tmp += QLatin1String(", "); + + tmp += m_arguments.at(i).toString(); + } + tmp += QLatin1String(")"); + } + + foreach(QString elt, arrayElements()) { + tmp += QLatin1String("["); + tmp += elt; + tmp += QLatin1String("]"); + } + + return tmp; +} + +bool TypeInfo::operator==(const TypeInfo &other) +{ + if (arrayElements().count() != other.arrayElements().count()) + return false; + +#if defined (RXX_CHECK_ARRAY_ELEMENTS) // ### it'll break + for (int i = 0; i < arrayElements().count(); ++i) { + QString elt1 = arrayElements().at(i).trimmed(); + QString elt2 = other.arrayElements().at(i).trimmed(); + + if (elt1 != elt2) + return false; + } +#endif + + return flags == other.flags + && m_qualifiedName == other.m_qualifiedName + && (!m_functionPointer || m_arguments == other.m_arguments); +} + +// --------------------------------------------------------------------------- +_CodeModelItem::_CodeModelItem(CodeModel *model, int kind) + : _M_model(model), + _M_kind(kind), + _M_startLine(0), + _M_startColumn(0), + _M_endLine(0), + _M_endColumn(0), + _M_creation_id(0) +{ +} + +_CodeModelItem::~_CodeModelItem() +{ +} + +CodeModelItem _CodeModelItem::toItem() const +{ + return CodeModelItem(const_cast<_CodeModelItem*>(this)); +} + +int _CodeModelItem::kind() const +{ + return _M_kind; +} + +void _CodeModelItem::setKind(int kind) +{ + _M_kind = kind; +} + +QStringList _CodeModelItem::qualifiedName() const +{ + QStringList q = scope(); + + if (!name().isEmpty()) + q += name(); + + return q; +} + +QString _CodeModelItem::name() const +{ + return _M_name; +} + +void _CodeModelItem::setName(const QString &name) +{ + _M_name = name; +} + +QStringList _CodeModelItem::scope() const +{ + return _M_scope; +} + +void _CodeModelItem::setScope(const QStringList &scope) +{ + _M_scope = scope; +} + +QString _CodeModelItem::fileName() const +{ + return _M_fileName; +} + +void _CodeModelItem::setFileName(const QString &fileName) +{ + _M_fileName = fileName; +} + +FileModelItem _CodeModelItem::file() const +{ + return model()->findFile(fileName()); +} + +void _CodeModelItem::getStartPosition(int *line, int *column) +{ + *line = _M_startLine; + *column = _M_startColumn; +} + +void _CodeModelItem::setStartPosition(int line, int column) +{ + _M_startLine = line; + _M_startColumn = column; +} + +void _CodeModelItem::getEndPosition(int *line, int *column) +{ + *line = _M_endLine; + *column = _M_endColumn; +} + +void _CodeModelItem::setEndPosition(int line, int column) +{ + _M_endLine = line; + _M_endColumn = column; +} + +// --------------------------------------------------------------------------- +QStringList _ClassModelItem::baseClasses() const +{ + return _M_baseClasses; +} + +void _ClassModelItem::setBaseClasses(const QStringList &baseClasses) +{ + _M_baseClasses = baseClasses; +} + +TemplateParameterList _ClassModelItem::templateParameters() const +{ + return _M_templateParameters; +} + +void _ClassModelItem::setTemplateParameters(const TemplateParameterList &templateParameters) +{ + _M_templateParameters = templateParameters; +} + +void _ClassModelItem::addBaseClass(const QString &baseClass) +{ + _M_baseClasses.append(baseClass); +} + +void _ClassModelItem::removeBaseClass(const QString &baseClass) +{ + _M_baseClasses.removeAt(_M_baseClasses.indexOf(baseClass)); +} + +bool _ClassModelItem::extendsClass(const QString &name) const +{ + return _M_baseClasses.contains(name); +} + +void _ClassModelItem::setClassType(CodeModel::ClassType type) +{ + _M_classType = type; +} + +CodeModel::ClassType _ClassModelItem::classType() const +{ + return _M_classType; +} + +void _ClassModelItem::addPropertyDeclaration(const QString &propertyDeclaration) +{ + _M_propertyDeclarations << propertyDeclaration; +} + + +// --------------------------------------------------------------------------- +FunctionModelItem _ScopeModelItem::declaredFunction(FunctionModelItem item) +{ + FunctionList function_list = findFunctions(item->name()); + + foreach(FunctionModelItem fun, function_list) { + if (fun->isSimilar(item)) + return fun; + } + + return FunctionModelItem(); +} + +ClassList _ScopeModelItem::classes() const +{ + return _M_classes.values(); +} + +TypeAliasList _ScopeModelItem::typeAliases() const +{ + return _M_typeAliases.values(); +} + +VariableList _ScopeModelItem::variables() const +{ + return _M_variables.values(); +} + +FunctionList _ScopeModelItem::functions() const +{ + return _M_functions.values(); +} + +void _ScopeModelItem::addEnumsDeclaration(const QString &enumsDeclaration) +{ + _M_enumsDeclarations << enumsDeclaration; +} + +FunctionDefinitionList _ScopeModelItem::functionDefinitions() const +{ + return _M_functionDefinitions.values(); +} + +EnumList _ScopeModelItem::enums() const +{ + return _M_enums.values(); +} + +void _ScopeModelItem::addClass(ClassModelItem item) +{ + QString name = item->name(); + int idx = name.indexOf("<"); + if (idx > 0) + _M_classes.insert(name.left(idx), item); + _M_classes.insert(name, item); +} + +void _ScopeModelItem::addFunction(FunctionModelItem item) +{ + _M_functions.insert(item->name(), item); +} + +void _ScopeModelItem::addFunctionDefinition(FunctionDefinitionModelItem item) +{ + _M_functionDefinitions.insert(item->name(), item); +} + +void _ScopeModelItem::addVariable(VariableModelItem item) +{ + _M_variables.insert(item->name(), item); +} + +void _ScopeModelItem::addTypeAlias(TypeAliasModelItem item) +{ + _M_typeAliases.insert(item->name(), item); +} + +void _ScopeModelItem::addEnum(EnumModelItem item) +{ + _M_enums.insert(item->name(), item); +} + +void _ScopeModelItem::removeClass(ClassModelItem item) +{ + QHash::Iterator it = _M_classes.find(item->name()); + + if (it != _M_classes.end() && it.value() == item) + _M_classes.erase(it); +} + +void _ScopeModelItem::removeFunction(FunctionModelItem item) +{ + QMultiHash::Iterator it = _M_functions.find(item->name()); + + while (it != _M_functions.end() && it.key() == item->name() + && it.value() != item) { + ++it; + } + + if (it != _M_functions.end() && it.value() == item) { + _M_functions.erase(it); + } +} + +void _ScopeModelItem::removeFunctionDefinition(FunctionDefinitionModelItem item) +{ + QMultiHash::Iterator it = _M_functionDefinitions.find(item->name()); + + while (it != _M_functionDefinitions.end() && it.key() == item->name() + && it.value() != item) { + ++it; + } + + if (it != _M_functionDefinitions.end() && it.value() == item) { + _M_functionDefinitions.erase(it); + } +} + +void _ScopeModelItem::removeVariable(VariableModelItem item) +{ + QHash::Iterator it = _M_variables.find(item->name()); + + if (it != _M_variables.end() && it.value() == item) + _M_variables.erase(it); +} + +void _ScopeModelItem::removeTypeAlias(TypeAliasModelItem item) +{ + QHash::Iterator it = _M_typeAliases.find(item->name()); + + if (it != _M_typeAliases.end() && it.value() == item) + _M_typeAliases.erase(it); +} + +void _ScopeModelItem::removeEnum(EnumModelItem item) +{ + QHash::Iterator it = _M_enums.find(item->name()); + + if (it != _M_enums.end() && it.value() == item) + _M_enums.erase(it); +} + +ClassModelItem _ScopeModelItem::findClass(const QString &name) const +{ + return _M_classes.value(name); +} + +VariableModelItem _ScopeModelItem::findVariable(const QString &name) const +{ + return _M_variables.value(name); +} + +TypeAliasModelItem _ScopeModelItem::findTypeAlias(const QString &name) const +{ + return _M_typeAliases.value(name); +} + +EnumModelItem _ScopeModelItem::findEnum(const QString &name) const +{ + return _M_enums.value(name); +} + +FunctionList _ScopeModelItem::findFunctions(const QString &name) const +{ + return _M_functions.values(name); +} + +FunctionDefinitionList _ScopeModelItem::findFunctionDefinitions(const QString &name) const +{ + return _M_functionDefinitions.values(name); +} + +// --------------------------------------------------------------------------- +NamespaceList _NamespaceModelItem::namespaces() const +{ + return _M_namespaces.values(); +} +void _NamespaceModelItem::addNamespace(NamespaceModelItem item) +{ + _M_namespaces.insert(item->name(), item); +} +void _NamespaceModelItem::removeNamespace(NamespaceModelItem item) +{ + QHash::Iterator it = _M_namespaces.find(item->name()); + + if (it != _M_namespaces.end() && it.value() == item) + _M_namespaces.erase(it); +} + +NamespaceModelItem _NamespaceModelItem::findNamespace(const QString &name) const +{ + return _M_namespaces.value(name); +} + +// --------------------------------------------------------------------------- +TypeInfo _ArgumentModelItem::type() const +{ + return _M_type; +} + +void _ArgumentModelItem::setType(const TypeInfo &type) +{ + _M_type = type; +} + +bool _ArgumentModelItem::defaultValue() const +{ + return _M_defaultValue; +} + +void _ArgumentModelItem::setDefaultValue(bool defaultValue) +{ + _M_defaultValue = defaultValue; +} + +// --------------------------------------------------------------------------- +bool _FunctionModelItem::isSimilar(FunctionModelItem other) const +{ + if (name() != other->name()) + return false; + + if (isConstant() != other->isConstant()) + return false; + + if (isVariadics() != other->isVariadics()) + return false; + + if (arguments().count() != other->arguments().count()) + return false; + + // ### check the template parameters + + for (int i = 0; i < arguments().count(); ++i) { + ArgumentModelItem arg1 = arguments().at(i); + ArgumentModelItem arg2 = other->arguments().at(i); + + if (arg1->type() != arg2->type()) + return false; + } + + return true; +} + +ArgumentList _FunctionModelItem::arguments() const +{ + return _M_arguments; +} + +void _FunctionModelItem::addArgument(ArgumentModelItem item) +{ + _M_arguments.append(item); +} + +void _FunctionModelItem::removeArgument(ArgumentModelItem item) +{ + _M_arguments.removeAt(_M_arguments.indexOf(item)); +} + +CodeModel::FunctionType _FunctionModelItem::functionType() const +{ + return _M_functionType; +} + +void _FunctionModelItem::setFunctionType(CodeModel::FunctionType functionType) +{ + _M_functionType = functionType; +} + +bool _FunctionModelItem::isVariadics() const +{ + return _M_isVariadics; +} + +void _FunctionModelItem::setVariadics(bool isVariadics) +{ + _M_isVariadics = isVariadics; +} + +bool _FunctionModelItem::isVirtual() const +{ + return _M_isVirtual; +} + +void _FunctionModelItem::setVirtual(bool isVirtual) +{ + _M_isVirtual = isVirtual; +} + +bool _FunctionModelItem::isInline() const +{ + return _M_isInline; +} + +void _FunctionModelItem::setInline(bool isInline) +{ + _M_isInline = isInline; +} + +bool _FunctionModelItem::isExplicit() const +{ + return _M_isExplicit; +} + +void _FunctionModelItem::setExplicit(bool isExplicit) +{ + _M_isExplicit = isExplicit; +} + +bool _FunctionModelItem::isAbstract() const +{ + return _M_isAbstract; +} + +void _FunctionModelItem::setAbstract(bool isAbstract) +{ + _M_isAbstract = isAbstract; +} + +// Qt +bool _FunctionModelItem::isInvokable() const +{ + return _M_isInvokable; +} + +void _FunctionModelItem::setInvokable(bool isInvokable) +{ + _M_isInvokable = isInvokable; +} + +// --------------------------------------------------------------------------- +TypeInfo _TypeAliasModelItem::type() const +{ + return _M_type; +} + +void _TypeAliasModelItem::setType(const TypeInfo &type) +{ + _M_type = type; +} + +// --------------------------------------------------------------------------- +CodeModel::AccessPolicy _EnumModelItem::accessPolicy() const +{ + return _M_accessPolicy; +} + +void _EnumModelItem::setAccessPolicy(CodeModel::AccessPolicy accessPolicy) +{ + _M_accessPolicy = accessPolicy; +} + +EnumeratorList _EnumModelItem::enumerators() const +{ + return _M_enumerators; +} + +void _EnumModelItem::addEnumerator(EnumeratorModelItem item) +{ + _M_enumerators.append(item); +} + +void _EnumModelItem::removeEnumerator(EnumeratorModelItem item) +{ + _M_enumerators.removeAt(_M_enumerators.indexOf(item)); +} + +// --------------------------------------------------------------------------- +QString _EnumeratorModelItem::value() const +{ + return _M_value; +} + +void _EnumeratorModelItem::setValue(const QString &value) +{ + _M_value = value; +} + +// --------------------------------------------------------------------------- +TypeInfo _TemplateParameterModelItem::type() const +{ + return _M_type; +} + +void _TemplateParameterModelItem::setType(const TypeInfo &type) +{ + _M_type = type; +} + +bool _TemplateParameterModelItem::defaultValue() const +{ + return _M_defaultValue; +} + +void _TemplateParameterModelItem::setDefaultValue(bool defaultValue) +{ + _M_defaultValue = defaultValue; +} + +// --------------------------------------------------------------------------- +ScopeModelItem _ScopeModelItem::create(CodeModel *model) +{ + ScopeModelItem item(new _ScopeModelItem(model)); + return item; +} + +ClassModelItem _ClassModelItem::create(CodeModel *model) +{ + ClassModelItem item(new _ClassModelItem(model)); + return item; +} + +NamespaceModelItem _NamespaceModelItem::create(CodeModel *model) +{ + NamespaceModelItem item(new _NamespaceModelItem(model)); + return item; +} + +FileModelItem _FileModelItem::create(CodeModel *model) +{ + FileModelItem item(new _FileModelItem(model)); + return item; +} + +ArgumentModelItem _ArgumentModelItem::create(CodeModel *model) +{ + ArgumentModelItem item(new _ArgumentModelItem(model)); + return item; +} + +FunctionModelItem _FunctionModelItem::create(CodeModel *model) +{ + FunctionModelItem item(new _FunctionModelItem(model)); + return item; +} + +FunctionDefinitionModelItem _FunctionDefinitionModelItem::create(CodeModel *model) +{ + FunctionDefinitionModelItem item(new _FunctionDefinitionModelItem(model)); + return item; +} + +VariableModelItem _VariableModelItem::create(CodeModel *model) +{ + VariableModelItem item(new _VariableModelItem(model)); + return item; +} + +TypeAliasModelItem _TypeAliasModelItem::create(CodeModel *model) +{ + TypeAliasModelItem item(new _TypeAliasModelItem(model)); + return item; +} + +EnumModelItem _EnumModelItem::create(CodeModel *model) +{ + EnumModelItem item(new _EnumModelItem(model)); + return item; +} + +EnumeratorModelItem _EnumeratorModelItem::create(CodeModel *model) +{ + EnumeratorModelItem item(new _EnumeratorModelItem(model)); + return item; +} + +TemplateParameterModelItem _TemplateParameterModelItem::create(CodeModel *model) +{ + TemplateParameterModelItem item(new _TemplateParameterModelItem(model)); + return item; +} + +// --------------------------------------------------------------------------- +TypeInfo _MemberModelItem::type() const +{ + return _M_type; +} + +void _MemberModelItem::setType(const TypeInfo &type) +{ + _M_type = type; +} + +CodeModel::AccessPolicy _MemberModelItem::accessPolicy() const +{ + return _M_accessPolicy; +} + +void _MemberModelItem::setAccessPolicy(CodeModel::AccessPolicy accessPolicy) +{ + _M_accessPolicy = accessPolicy; +} + +bool _MemberModelItem::isStatic() const +{ + return _M_isStatic; +} + +void _MemberModelItem::setStatic(bool isStatic) +{ + _M_isStatic = isStatic; +} + +bool _MemberModelItem::isConstant() const +{ + return _M_isConstant; +} + +void _MemberModelItem::setConstant(bool isConstant) +{ + _M_isConstant = isConstant; +} + +bool _MemberModelItem::isVolatile() const +{ + return _M_isVolatile; +} + +void _MemberModelItem::setVolatile(bool isVolatile) +{ + _M_isVolatile = isVolatile; +} + +bool _MemberModelItem::isAuto() const +{ + return _M_isAuto; +} + +void _MemberModelItem::setAuto(bool isAuto) +{ + _M_isAuto = isAuto; +} + +bool _MemberModelItem::isFriend() const +{ + return _M_isFriend; +} + +void _MemberModelItem::setFriend(bool isFriend) +{ + _M_isFriend = isFriend; +} + +bool _MemberModelItem::isRegister() const +{ + return _M_isRegister; +} + +void _MemberModelItem::setRegister(bool isRegister) +{ + _M_isRegister = isRegister; +} + +bool _MemberModelItem::isExtern() const +{ + return _M_isExtern; +} + +void _MemberModelItem::setExtern(bool isExtern) +{ + _M_isExtern = isExtern; +} + +bool _MemberModelItem::isMutable() const +{ + return _M_isMutable; +} + +void _MemberModelItem::setMutable(bool isMutable) +{ + _M_isMutable = isMutable; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/codemodel.h b/parser/codemodel.h new file mode 100644 index 000000000..3ea94480f --- /dev/null +++ b/parser/codemodel.h @@ -0,0 +1,838 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef CODEMODEL_H +#define CODEMODEL_H + +#include "codemodel_fwd.h" +#include "codemodel_pointer.h" + +#include +#include +#include +#include +#include + +#define DECLARE_MODEL_NODE(k) \ + enum { __node_kind = Kind_##k }; \ + typedef CodeModelPointer Pointer; + +template +_Target model_static_cast(_Source item) +{ + typedef typename _Target::Type * _Target_pointer; + + _Target ptr(static_cast<_Target_pointer>(item.data())); + return ptr; +} + +class CodeModel +{ +public: + enum AccessPolicy { + Public, + Protected, + Private + }; + + enum FunctionType { + Normal, + Signal, + Slot + }; + + enum ClassType { + Class, + Struct, + Union + }; + +public: + CodeModel(); + virtual ~CodeModel(); + + template _Target create() + { + typedef typename _Target::Type _Target_type; + + _Target result = _Target_type::create(this); + result->setCreationId(_M_creation_id++); + return result; + } + + FileList files() const; + NamespaceModelItem globalNamespace() const; + + void addFile(FileModelItem item); + void removeFile(FileModelItem item); + FileModelItem findFile(const QString &name) const; + QHash fileMap() const; + + CodeModelItem findItem(const QStringList &qualifiedName, CodeModelItem scope) const; + + void wipeout(); + +private: + QHash _M_files; + NamespaceModelItem _M_globalNamespace; + std::size_t _M_creation_id; + +private: + CodeModel(const CodeModel &other); + void operator = (const CodeModel &other); +}; + +class TypeInfo +{ +public: + TypeInfo(const TypeInfo &other) + : flags(other.flags), + m_qualifiedName(other.m_qualifiedName), + m_arrayElements(other.m_arrayElements), + m_arguments(other.m_arguments) + {} + + TypeInfo(): + flags(0) {} + + QStringList qualifiedName() const + { + return m_qualifiedName; + } + + void setQualifiedName(const QStringList &qualified_name) + { + m_qualifiedName = qualified_name; + } + + bool isConstant() const + { + return m_constant; + } + + void setConstant(bool is) + { + m_constant = is; + } + + bool isVolatile() const + { + return m_volatile; + } + + void setVolatile(bool is) + { + m_volatile = is; + } + + bool isReference() const + { + return m_reference; + } + + void setReference(bool is) + { + m_reference = is; + } + + int indirections() const + { + return m_indirections; + } + + void setIndirections(int indirections) + { + m_indirections = indirections; + } + + bool isFunctionPointer() const + { + return m_functionPointer; + } + void setFunctionPointer(bool is) + { + m_functionPointer = is; + } + + QStringList arrayElements() const + { + return m_arrayElements; + } + void setArrayElements(const QStringList &arrayElements) + { + m_arrayElements = arrayElements; + } + + QList arguments() const + { + return m_arguments; + } + + void setArguments(const QList &arguments); + + void addArgument(const TypeInfo &arg) + { + m_arguments.append(arg); + } + + bool operator==(const TypeInfo &other); + + bool operator!=(const TypeInfo &other) + { + return !(*this == other); + } + + // ### arrays and templates?? + + QString toString() const; + + static TypeInfo combine(const TypeInfo &__lhs, const TypeInfo &__rhs); + static TypeInfo resolveType(TypeInfo const &__type, CodeModelItem __scope); + +private: + union { + uint flags; + + struct { + uint m_constant: 1; + uint m_volatile: 1; + uint m_reference: 1; + uint m_functionPointer: 1; + uint m_indirections: 6; + uint m_padding: 22; + }; + }; + + QStringList m_qualifiedName; + QStringList m_arrayElements; + QList m_arguments; +}; + +class _CodeModelItem: public QSharedData +{ +public: + enum Kind { + /* These are bit-flags resembling inheritance */ + Kind_Scope = 0x1, + Kind_Namespace = 0x2 | Kind_Scope, + Kind_Member = 0x4, + Kind_Function = 0x8 | Kind_Member, + KindMask = 0xf, + + /* These are for classes that are not inherited from */ + FirstKind = 0x8, + Kind_Argument = 1 << FirstKind, + Kind_Class = 2 << FirstKind | Kind_Scope, + Kind_Enum = 3 << FirstKind, + Kind_Enumerator = 4 << FirstKind, + Kind_File = 5 << FirstKind | Kind_Namespace, + Kind_FunctionDefinition = 6 << FirstKind | Kind_Function, + Kind_TemplateParameter = 7 << FirstKind, + Kind_TypeAlias = 8 << FirstKind, + Kind_Variable = 9 << FirstKind | Kind_Member + }; + +public: + virtual ~_CodeModelItem(); + + int kind() const; + + QStringList qualifiedName() const; + + QString name() const; + void setName(const QString &name); + + QStringList scope() const; + void setScope(const QStringList &scope); + + QString fileName() const; + void setFileName(const QString &fileName); + + FileModelItem file() const; + + void getStartPosition(int *line, int *column); + void setStartPosition(int line, int column); + + void getEndPosition(int *line, int *column); + void setEndPosition(int line, int column); + + inline std::size_t creationId() const + { + return _M_creation_id; + } + + inline void setCreationId(std::size_t creation_id) + { + _M_creation_id = creation_id; + } + + inline CodeModel *model() const + { + return _M_model; + } + + CodeModelItem toItem() const; + +protected: + _CodeModelItem(CodeModel *model, int kind); + void setKind(int kind); + +private: + CodeModel *_M_model; + int _M_kind; + int _M_startLine; + int _M_startColumn; + int _M_endLine; + int _M_endColumn; + std::size_t _M_creation_id; + QString _M_name; + QString _M_fileName; + QStringList _M_scope; + +private: + _CodeModelItem(const _CodeModelItem &other); + void operator = (const _CodeModelItem &other); +}; + +class _ScopeModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(Scope) + + static ScopeModelItem create(CodeModel *model); + +public: + ClassList classes() const; + EnumList enums() const; + FunctionDefinitionList functionDefinitions() const; + FunctionList functions() const; + TypeAliasList typeAliases() const; + VariableList variables() const; + + void addClass(ClassModelItem item); + void addEnum(EnumModelItem item); + void addFunction(FunctionModelItem item); + void addFunctionDefinition(FunctionDefinitionModelItem item); + void addTypeAlias(TypeAliasModelItem item); + void addVariable(VariableModelItem item); + + void removeClass(ClassModelItem item); + void removeEnum(EnumModelItem item); + void removeFunction(FunctionModelItem item); + void removeFunctionDefinition(FunctionDefinitionModelItem item); + void removeTypeAlias(TypeAliasModelItem item); + void removeVariable(VariableModelItem item); + + ClassModelItem findClass(const QString &name) const; + EnumModelItem findEnum(const QString &name) const; + FunctionDefinitionList findFunctionDefinitions(const QString &name) const; + FunctionList findFunctions(const QString &name) const; + TypeAliasModelItem findTypeAlias(const QString &name) const; + VariableModelItem findVariable(const QString &name) const; + + void addEnumsDeclaration(const QString &enumsDeclaration); + QStringList enumsDeclarations() const + { + return _M_enumsDeclarations; + } + + inline QHash classMap() const + { + return _M_classes; + } + inline QHash enumMap() const + { + return _M_enums; + } + inline QHash typeAliasMap() const + { + return _M_typeAliases; + } + inline QHash variableMap() const + { + return _M_variables; + } + inline QMultiHash functionDefinitionMap() const + { + return _M_functionDefinitions; + } + inline QMultiHash functionMap() const + { + return _M_functions; + } + + FunctionModelItem declaredFunction(FunctionModelItem item); + +protected: + _ScopeModelItem(CodeModel *model, int kind = __node_kind) + : _CodeModelItem(model, kind) {} + +private: + QHash _M_classes; + QHash _M_enums; + QHash _M_typeAliases; + QHash _M_variables; + QMultiHash _M_functionDefinitions; + QMultiHash _M_functions; + +private: + _ScopeModelItem(const _ScopeModelItem &other); + void operator = (const _ScopeModelItem &other); + + QStringList _M_enumsDeclarations; +}; + +class _ClassModelItem: public _ScopeModelItem +{ +public: + DECLARE_MODEL_NODE(Class) + + static ClassModelItem create(CodeModel *model); + +public: + QStringList baseClasses() const; + + void setBaseClasses(const QStringList &baseClasses); + void addBaseClass(const QString &baseClass); + void removeBaseClass(const QString &baseClass); + + TemplateParameterList templateParameters() const; + void setTemplateParameters(const TemplateParameterList &templateParameters); + + bool extendsClass(const QString &name) const; + + void setClassType(CodeModel::ClassType type); + CodeModel::ClassType classType() const; + + void addPropertyDeclaration(const QString &propertyDeclaration); + QStringList propertyDeclarations() const + { + return _M_propertyDeclarations; + } + +protected: + _ClassModelItem(CodeModel *model, int kind = __node_kind) + : _ScopeModelItem(model, kind), _M_classType(CodeModel::Class) {} + +private: + QStringList _M_baseClasses; + TemplateParameterList _M_templateParameters; + CodeModel::ClassType _M_classType; + + QStringList _M_propertyDeclarations; + +private: + _ClassModelItem(const _ClassModelItem &other); + void operator = (const _ClassModelItem &other); +}; + +class _NamespaceModelItem: public _ScopeModelItem +{ +public: + DECLARE_MODEL_NODE(Namespace) + + static NamespaceModelItem create(CodeModel *model); + +public: + NamespaceList namespaces() const; + + void addNamespace(NamespaceModelItem item); + void removeNamespace(NamespaceModelItem item); + + NamespaceModelItem findNamespace(const QString &name) const; + + inline QHash namespaceMap() const + { + return _M_namespaces; + }; + +protected: + _NamespaceModelItem(CodeModel *model, int kind = __node_kind) + : _ScopeModelItem(model, kind) {} + +private: + QHash _M_namespaces; + +private: + _NamespaceModelItem(const _NamespaceModelItem &other); + void operator = (const _NamespaceModelItem &other); +}; + +class _FileModelItem: public _NamespaceModelItem +{ +public: + DECLARE_MODEL_NODE(File) + + static FileModelItem create(CodeModel *model); + +protected: + _FileModelItem(CodeModel *model, int kind = __node_kind) + : _NamespaceModelItem(model, kind) {} + +private: + _FileModelItem(const _FileModelItem &other); + void operator = (const _FileModelItem &other); +}; + +class _ArgumentModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(Argument) + + static ArgumentModelItem create(CodeModel *model); + +public: + TypeInfo type() const; + void setType(const TypeInfo &type); + + bool defaultValue() const; + void setDefaultValue(bool defaultValue); + + QString defaultValueExpression() const + { + return _M_defaultValueExpression; + } + + void setDefaultValueExpression(const QString &expr) + { + _M_defaultValueExpression = expr; + } + +protected: + _ArgumentModelItem(CodeModel *model, int kind = __node_kind) + : _CodeModelItem(model, kind), _M_defaultValue(false) {} + +private: + TypeInfo _M_type; + QString _M_defaultValueExpression; + bool _M_defaultValue; + +private: + _ArgumentModelItem(const _ArgumentModelItem &other); + void operator = (const _ArgumentModelItem &other); +}; + +class _MemberModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(Member) + + bool isConstant() const; + void setConstant(bool isConstant); + + bool isVolatile() const; + void setVolatile(bool isVolatile); + + bool isStatic() const; + void setStatic(bool isStatic); + + bool isAuto() const; + void setAuto(bool isAuto); + + bool isFriend() const; + void setFriend(bool isFriend); + + bool isRegister() const; + void setRegister(bool isRegister); + + bool isExtern() const; + void setExtern(bool isExtern); + + bool isMutable() const; + void setMutable(bool isMutable); + + CodeModel::AccessPolicy accessPolicy() const; + void setAccessPolicy(CodeModel::AccessPolicy accessPolicy); + + TemplateParameterList templateParameters() const + { + return _M_templateParameters; + } + + void setTemplateParameters(const TemplateParameterList &templateParameters) + { + _M_templateParameters = templateParameters; + } + + TypeInfo type() const; + void setType(const TypeInfo &type); + +protected: + _MemberModelItem(CodeModel *model, int kind) + : _CodeModelItem(model, kind), + _M_accessPolicy(CodeModel::Public), + _M_flags(0) {} + +private: + TemplateParameterList _M_templateParameters; + TypeInfo _M_type; + CodeModel::AccessPolicy _M_accessPolicy; + union { + struct { + uint _M_isConstant: 1; + uint _M_isVolatile: 1; + uint _M_isStatic: 1; + uint _M_isAuto: 1; + uint _M_isFriend: 1; + uint _M_isRegister: 1; + uint _M_isExtern: 1; + uint _M_isMutable: 1; + }; + uint _M_flags; + }; + +}; + +class _FunctionModelItem: public _MemberModelItem +{ +public: + DECLARE_MODEL_NODE(Function) + + static FunctionModelItem create(CodeModel *model); + +public: + ArgumentList arguments() const; + + void addArgument(ArgumentModelItem item); + void removeArgument(ArgumentModelItem item); + + CodeModel::FunctionType functionType() const; + void setFunctionType(CodeModel::FunctionType functionType); + + bool isVirtual() const; + void setVirtual(bool isVirtual); + + bool isInline() const; + void setInline(bool isInline); + + bool isExplicit() const; + void setExplicit(bool isExplicit); + + bool isInvokable() const; // Qt + void setInvokable(bool isInvokable); // Qt + + bool isAbstract() const; + void setAbstract(bool isAbstract); + + bool isVariadics() const; + void setVariadics(bool isVariadics); + + bool isSimilar(FunctionModelItem other) const; + +protected: + _FunctionModelItem(CodeModel *model, int kind = __node_kind) + : _MemberModelItem(model, kind), + _M_functionType(CodeModel::Normal), + _M_flags(0) {} + +private: + ArgumentList _M_arguments; + CodeModel::FunctionType _M_functionType; + union { + struct { + uint _M_isVirtual: 1; + uint _M_isInline: 1; + uint _M_isAbstract: 1; + uint _M_isExplicit: 1; + uint _M_isVariadics: 1; + uint _M_isInvokable : 1; // Qt + }; + uint _M_flags; + }; + +private: + _FunctionModelItem(const _FunctionModelItem &other); + void operator = (const _FunctionModelItem &other); +}; + +class _FunctionDefinitionModelItem: public _FunctionModelItem +{ +public: + DECLARE_MODEL_NODE(FunctionDefinition) + + static FunctionDefinitionModelItem create(CodeModel *model); + +protected: + _FunctionDefinitionModelItem(CodeModel *model, int kind = __node_kind) + : _FunctionModelItem(model, kind) {} + +private: + _FunctionDefinitionModelItem(const _FunctionDefinitionModelItem &other); + void operator = (const _FunctionDefinitionModelItem &other); +}; + +class _VariableModelItem: public _MemberModelItem +{ +public: + DECLARE_MODEL_NODE(Variable) + + static VariableModelItem create(CodeModel *model); + +protected: + _VariableModelItem(CodeModel *model, int kind = __node_kind) + : _MemberModelItem(model, kind) {} + +private: + _VariableModelItem(const _VariableModelItem &other); + void operator = (const _VariableModelItem &other); +}; + +class _TypeAliasModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(TypeAlias) + + static TypeAliasModelItem create(CodeModel *model); + +public: + TypeInfo type() const; + void setType(const TypeInfo &type); + +protected: + _TypeAliasModelItem(CodeModel *model, int kind = __node_kind) + : _CodeModelItem(model, kind) {} + +private: + TypeInfo _M_type; + +private: + _TypeAliasModelItem(const _TypeAliasModelItem &other); + void operator = (const _TypeAliasModelItem &other); +}; + +class _EnumModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(Enum) + + static EnumModelItem create(CodeModel *model); + +public: + CodeModel::AccessPolicy accessPolicy() const; + void setAccessPolicy(CodeModel::AccessPolicy accessPolicy); + + EnumeratorList enumerators() const; + void addEnumerator(EnumeratorModelItem item); + void removeEnumerator(EnumeratorModelItem item); + +protected: + _EnumModelItem(CodeModel *model, int kind = __node_kind) + : _CodeModelItem(model, kind), + _M_accessPolicy(CodeModel::Public) {} + +private: + CodeModel::AccessPolicy _M_accessPolicy; + EnumeratorList _M_enumerators; + +private: + _EnumModelItem(const _EnumModelItem &other); + void operator = (const _EnumModelItem &other); +}; + +class _EnumeratorModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(Enumerator) + + static EnumeratorModelItem create(CodeModel *model); + +public: + QString value() const; + void setValue(const QString &value); + +protected: + _EnumeratorModelItem(CodeModel *model, int kind = __node_kind) + : _CodeModelItem(model, kind) {} + +private: + QString _M_value; + +private: + _EnumeratorModelItem(const _EnumeratorModelItem &other); + void operator = (const _EnumeratorModelItem &other); +}; + +class _TemplateParameterModelItem: public _CodeModelItem +{ +public: + DECLARE_MODEL_NODE(TemplateParameter) + + static TemplateParameterModelItem create(CodeModel *model); + +public: + TypeInfo type() const; + void setType(const TypeInfo &type); + + bool defaultValue() const; + void setDefaultValue(bool defaultValue); + +protected: + _TemplateParameterModelItem(CodeModel *model, int kind = __node_kind) + : _CodeModelItem(model, kind), _M_defaultValue(false) {} + +private: + TypeInfo _M_type; + bool _M_defaultValue; + +private: + _TemplateParameterModelItem(const _TemplateParameterModelItem &other); + void operator = (const _TemplateParameterModelItem &other); +}; + +template +_Target model_safe_cast(_Source item) +{ + typedef typename _Target::Type * _Target_pointer; + typedef typename _Source::Type * _Source_pointer; + + _Source_pointer source = item.data(); + if (source && source->kind() == _Target_pointer(0)->__node_kind) { + _Target ptr(static_cast<_Target_pointer>(source)); + return ptr; + } + + return _Target(); +} + +template +_Target model_dynamic_cast(_Source item) +{ + typedef typename _Target::Type * _Target_pointer; + typedef typename _Source::Type * _Source_pointer; + + _Source_pointer source = item.data(); + if (source && (source->kind() == _Target_pointer(0)->__node_kind + || (int(_Target_pointer(0)->__node_kind) <= int(_CodeModelItem::KindMask) + && ((source->kind() & _Target_pointer(0)->__node_kind) + == _Target_pointer(0)->__node_kind)))) { + _Target ptr(static_cast<_Target_pointer>(source)); + return ptr; + } + + return _Target(); +} +#endif // CODEMODEL_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/codemodel_finder.cpp b/parser/codemodel_finder.cpp new file mode 100644 index 000000000..866ea1cbd --- /dev/null +++ b/parser/codemodel_finder.cpp @@ -0,0 +1,98 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "codemodel_finder.h" +#include "codemodel.h" +#include "binder.h" + +CodeModelFinder::CodeModelFinder(CodeModel *model, Binder *binder) + : _M_model(model), + _M_binder(binder), + _M_token_stream(binder->tokenStream()), + name_cc(_M_binder), + _M_resolve_policy(ResolveItem) +{ +} + +CodeModelFinder::~CodeModelFinder() +{ +} + +ScopeModelItem CodeModelFinder::resolveScope(NameAST *name, ScopeModelItem scope) +{ + Q_ASSERT(scope); + + ResolvePolicy saved_resolve_policy = _M_resolve_policy; + _M_resolve_policy = ResolveScope; + + ScopeModelItem old = changeCurrentScope(scope); + + visit(name); + ScopeModelItem result = _M_current_scope; + + changeCurrentScope(old); // restore + + _M_resolve_policy = saved_resolve_policy; + + return result; +} + +ScopeModelItem CodeModelFinder::changeCurrentScope(ScopeModelItem scope) +{ + ScopeModelItem old = _M_current_scope; + _M_current_scope = scope; + return old; +} + +void CodeModelFinder::visitName(NameAST *node) +{ + visitNodes(this, node->qualified_names); + + if (_M_resolve_policy == ResolveItem) + visit(node->unqualified_name); +} + +void CodeModelFinder::visitUnqualifiedName(UnqualifiedNameAST *node) +{ + if (!_M_current_scope) { + // nothing to do + return; + } + + name_cc.run(node); + QString id = name_cc.name(); + + if (ClassModelItem klass = _M_current_scope->findClass(id)) { + _M_current_scope = klass; + } else if (NamespaceModelItem parentNamespace = model_safe_cast(_M_current_scope)) { + NamespaceModelItem ns = parentNamespace->findNamespace(id); + _M_current_scope = model_static_cast(ns); + } else if (FileModelItem file = model_safe_cast(_M_current_scope)) { + NamespaceModelItem ns = file->findNamespace(id); + _M_current_scope = model_static_cast(ns); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/codemodel_finder.h b/parser/codemodel_finder.h new file mode 100644 index 000000000..ac1b5b0eb --- /dev/null +++ b/parser/codemodel_finder.h @@ -0,0 +1,70 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef CODEMODEL_FINDER_H +#define CODEMODEL_FINDER_H + +#include +#include +#include + +class TokenStream; +class Binder; + +class CodeModelFinder: protected DefaultVisitor +{ + enum ResolvePolicy { + ResolveScope, + ResolveItem + }; + +public: + CodeModelFinder(CodeModel *model, Binder *binder); + virtual ~CodeModelFinder(); + + ScopeModelItem resolveScope(NameAST *name, ScopeModelItem scope); + + inline CodeModel *model() const + { + return _M_model; + } + +protected: + virtual void visitName(NameAST *node); + virtual void visitUnqualifiedName(UnqualifiedNameAST *node); + + ScopeModelItem changeCurrentScope(ScopeModelItem scope); + +private: + CodeModel *_M_model; + Binder *_M_binder; + TokenStream *_M_token_stream; + NameCompiler name_cc; + + ScopeModelItem _M_current_scope; + ResolvePolicy _M_resolve_policy; +}; + +#endif // CODEMODEL_FINDER_H diff --git a/parser/codemodel_fwd.h b/parser/codemodel_fwd.h new file mode 100644 index 000000000..96405909c --- /dev/null +++ b/parser/codemodel_fwd.h @@ -0,0 +1,80 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef CODEMODEL_FWD_H +#define CODEMODEL_FWD_H + +#include "codemodel_pointer.h" +#include + +// forward declarations +class CodeModel; +class _ArgumentModelItem; +class _ClassModelItem; +class _CodeModelItem; +class _EnumModelItem; +class _EnumeratorModelItem; +class _FileModelItem; +class _FunctionDefinitionModelItem; +class _FunctionModelItem; +class _NamespaceModelItem; +class _ScopeModelItem; +class _TemplateParameterModelItem; +class _TypeAliasModelItem; +class _VariableModelItem; +class _MemberModelItem; +class TypeInfo; + +typedef CodeModelPointer<_ArgumentModelItem> ArgumentModelItem; +typedef CodeModelPointer<_ClassModelItem> ClassModelItem; +typedef CodeModelPointer<_CodeModelItem> CodeModelItem; +typedef CodeModelPointer<_EnumModelItem> EnumModelItem; +typedef CodeModelPointer<_EnumeratorModelItem> EnumeratorModelItem; +typedef CodeModelPointer<_FileModelItem> FileModelItem; +typedef CodeModelPointer<_FunctionDefinitionModelItem> FunctionDefinitionModelItem; +typedef CodeModelPointer<_FunctionModelItem> FunctionModelItem; +typedef CodeModelPointer<_NamespaceModelItem> NamespaceModelItem; +typedef CodeModelPointer<_ScopeModelItem> ScopeModelItem; +typedef CodeModelPointer<_TemplateParameterModelItem> TemplateParameterModelItem; +typedef CodeModelPointer<_TypeAliasModelItem> TypeAliasModelItem; +typedef CodeModelPointer<_VariableModelItem> VariableModelItem; +typedef CodeModelPointer<_MemberModelItem> MemberModelItem; + +typedef QList ArgumentList; +typedef QList ClassList; +typedef QList ItemList; +typedef QList EnumList; +typedef QList EnumeratorList; +typedef QList FileList; +typedef QList FunctionDefinitionList; +typedef QList FunctionList; +typedef QList NamespaceList; +typedef QList ScopeList; +typedef QList TemplateParameterList; +typedef QList TypeAliasList; +typedef QList VariableList; +typedef QList MemberList; + +#endif // CODEMODEL_FWD_H diff --git a/parser/codemodel_pointer.h b/parser/codemodel_pointer.h new file mode 100644 index 000000000..2c728f3c5 --- /dev/null +++ b/parser/codemodel_pointer.h @@ -0,0 +1,60 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2006 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef CODEMODEL_POINTER_H +#define CODEMODEL_POINTER_H + +#include +#include + +template class CodeModelPointer : public QAtomicPointer +{ +public: + typedef T Type; + + inline CodeModelPointer(T *value = 0) : QAtomicPointer(value) {} + + inline CodeModelPointer &operator=(T *o) + { + QAtomicPointer::operator=(o); + return *this; + } + + inline T *data() + { + return (T *) *this; + } + + inline const T *data() const + { + return (const T *) *this; + } + + inline const T *constData() const + { + return (const T *) *this; + } +}; + +#endif // CODEMODEL_POINTER_H diff --git a/parser/compiler_utils.cpp b/parser/compiler_utils.cpp new file mode 100644 index 000000000..49624149a --- /dev/null +++ b/parser/compiler_utils.cpp @@ -0,0 +1,50 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "compiler_utils.h" +#include "type_compiler.h" +#include "name_compiler.h" +#include "declarator_compiler.h" +#include "ast.h" +#include "binder.h" + +TypeInfo CompilerUtils::typeDescription(TypeSpecifierAST *type_specifier, DeclaratorAST *declarator, Binder *binder) +{ + TypeCompiler type_cc(binder); + DeclaratorCompiler decl_cc(binder); + + type_cc.run(type_specifier); + decl_cc.run(declarator); + + TypeInfo typeInfo; + typeInfo.setQualifiedName(type_cc.qualifiedName()); + typeInfo.setConstant(type_cc.isConstant()); + typeInfo.setVolatile(type_cc.isVolatile()); + typeInfo.setReference(decl_cc.isReference()); + typeInfo.setIndirections(decl_cc.indirection()); + typeInfo.setArrayElements(decl_cc.arrayElements()); + + return typeInfo; +} diff --git a/parser/compiler_utils.h b/parser/compiler_utils.h new file mode 100644 index 000000000..fdd96f15b --- /dev/null +++ b/parser/compiler_utils.h @@ -0,0 +1,47 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef COMPILER_UTILS_H +#define COMPILER_UTILS_H + +#include + +#include "codemodel.h" + +class QString; +class QStringList; +struct TypeSpecifierAST; +struct DeclaratorAST; +class TokenStream; +class Binder; + +namespace CompilerUtils +{ + +TypeInfo typeDescription(TypeSpecifierAST *type_specifier, DeclaratorAST *declarator, Binder *binder); + +} // namespace CompilerUtils + +#endif // COMPILER_UTILS_H diff --git a/parser/control.cpp b/parser/control.cpp new file mode 100644 index 000000000..9615debba --- /dev/null +++ b/parser/control.cpp @@ -0,0 +1,130 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "control.h" +#include "lexer.h" + +Control::Control() + : current_context(0), + _M_skipFunctionBody(false), + _M_lexer(0), + _M_parser(0) +{ + pushContext(); + + declareTypedef(findOrInsertName("__builtin_va_list", + strlen("__builtin_va_list")), 0); +} + +Control::~Control() +{ + popContext(); + + Q_ASSERT(current_context == 0); +} + +Lexer *Control::changeLexer(Lexer *lexer) +{ + Lexer *old = _M_lexer; + _M_lexer = lexer; + return old; +} + +Parser *Control::changeParser(Parser *parser) +{ + Parser *old = _M_parser; + _M_parser = parser; + return old; +} + +Type *Control::lookupType(const NameSymbol *name) const +{ + Q_ASSERT(current_context != 0); + + return current_context->resolve(name); +} + +void Control::declare(const NameSymbol *name, Type *type) +{ + //printf("*** Declare:"); + //printSymbol(name); + //putchar('\n'); + Q_ASSERT(current_context != 0); + + current_context->bind(name, type); +} + +void Control::pushContext() +{ + // printf("+Context\n"); + Context *new_context = new Context; + new_context->parent = current_context; + current_context = new_context; +} + +void Control::popContext() +{ + // printf("-Context\n"); + Q_ASSERT(current_context != 0); + + Context *old_context = current_context; + current_context = current_context->parent; + + delete old_context; +} + +void Control::declareTypedef(const NameSymbol *name, Declarator *d) +{ + // printf("declared typedef:"); + // printSymbol(name); + // printf("\n"); + stl_typedef_table.insert(name, d); +} + +bool Control::isTypedef(const NameSymbol *name) const +{ + // printf("is typedef:"); + // printSymbol(name); + // printf("= %d\n", (stl_typedef_table.find(name) != stl_typedef_table.end())); + + return stl_typedef_table.contains(name); +} + +QList Control::errorMessages() const +{ + return _M_error_messages; +} + +void Control::clearErrorMessages() +{ + _M_error_messages.clear(); +} + +void Control::reportError(const ErrorMessage &errmsg) +{ + _M_error_messages.append(errmsg); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/control.h b/parser/control.h new file mode 100644 index 000000000..533db912c --- /dev/null +++ b/parser/control.h @@ -0,0 +1,160 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef CONTROL_H +#define CONTROL_H + +#include "symbol.h" +#include "smallobject.h" + +#include + +struct Declarator; +struct Type; +class Lexer; +class Parser; + +struct Context { + Context *parent; + + inline void bind(const NameSymbol *name, Type *type) { + symbol_table.insert(name, type); + } + + inline Type *resolve(const NameSymbol *name) const { + if (Type *type = symbol_table.value(name)) + return type; + else if (parent) + return parent->resolve(name); + + return 0; + } + + typedef QHash symbol_table_t; + + symbol_table_t symbol_table; +}; + +class Control +{ +public: + class ErrorMessage + { + public: + ErrorMessage(): + _M_line(0), + _M_column(0) {} + + inline int line() const { + return _M_line; + } + inline void setLine(int line) { + _M_line = line; + } + + inline int column() const { + return _M_column; + } + inline void setColumn(int column) { + _M_column = column; + } + + inline QString fileName() const { + return _M_fileName; + } + inline void setFileName(const QString &fileName) { + _M_fileName = fileName; + } + + inline QString message() const { + return _M_message; + } + inline void setMessage(const QString &message) { + _M_message = message; + } + + private: + int _M_line; + int _M_column; + QString _M_fileName; + QString _M_message; + }; + + Control(); + ~Control(); + + inline bool skipFunctionBody() const { + return _M_skipFunctionBody; + } + inline void setSkipFunctionBody(bool skip) { + _M_skipFunctionBody = skip; + } + + Lexer *changeLexer(Lexer *lexer); + Parser *changeParser(Parser *parser); + + Lexer *currentLexer() const { + return _M_lexer; + } + Parser *currentParser() const { + return _M_parser; + } + + Context *current_context; + + inline Context *currentContext() const { + return current_context; + } + + void pushContext(); + void popContext(); + + Type *lookupType(const NameSymbol *name) const; + void declare(const NameSymbol *name, Type *type); + + inline const NameSymbol *findOrInsertName(const char *data, size_t count) { + return name_table.findOrInsert(data, count); + } + + void declareTypedef(const NameSymbol *name, Declarator *d); + bool isTypedef(const NameSymbol *name) const; + + void reportError(const ErrorMessage &errmsg); + QList errorMessages() const; + void clearErrorMessages(); + +private: + NameTable name_table; + QHash stl_typedef_table; + bool _M_skipFunctionBody; + Lexer *_M_lexer; + Parser *_M_parser; + + QList _M_error_messages; +}; + +#endif // CONTROL_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/declarator_compiler.cpp b/parser/declarator_compiler.cpp new file mode 100644 index 000000000..255bfcddb --- /dev/null +++ b/parser/declarator_compiler.cpp @@ -0,0 +1,147 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "declarator_compiler.h" +#include "name_compiler.h" +#include "type_compiler.h" +#include "compiler_utils.h" +#include "lexer.h" +#include "binder.h" +#include "tokens.h" + +#include + +DeclaratorCompiler::DeclaratorCompiler(Binder *binder) + : _M_binder(binder), _M_token_stream(binder->tokenStream()) +{ +} + +void DeclaratorCompiler::run(DeclaratorAST *node) +{ + _M_id.clear(); + _M_parameters.clear(); + _M_array.clear(); + _M_function = false; + _M_reference = false; + _M_variadics = false; + _M_indirection = 0; + + if (node) { + NameCompiler name_cc(_M_binder); + + DeclaratorAST *decl = node; + while (decl && decl->sub_declarator) + decl = decl->sub_declarator; + + Q_ASSERT(decl != 0); + + name_cc.run(decl->id); + _M_id = name_cc.name(); + _M_function = (node->parameter_declaration_clause != 0); + if (node->parameter_declaration_clause && node->parameter_declaration_clause->ellipsis) + _M_variadics = true; + + visitNodes(this, node->ptr_ops); + visit(node->parameter_declaration_clause); + + if (const ListNode *it = node->array_dimensions) { + it->toFront(); + const ListNode *end = it; + + do { + QString elt; + if (ExpressionAST *expr = it->element) { + const Token &start_token = _M_token_stream->token((int) expr->start_token); + const Token &end_token = _M_token_stream->token((int) expr->end_token); + + elt += QString::fromUtf8(&start_token.text[start_token.position], + (int)(end_token.position - start_token.position)).trimmed(); + } + + _M_array.append(elt); + + it = it->next; + } while (it != end); + } + } +} + +void DeclaratorCompiler::visitPtrOperator(PtrOperatorAST *node) +{ + std::size_t op = _M_token_stream->kind(node->op); + + switch (op) { + case '&': + _M_reference = true; + break; + case '*': + ++_M_indirection; + break; + + default: + break; + } + + if (node->mem_ptr) { +#if defined(__GNUC__) +#warning "ptr to mem -- not implemented" +#endif + } +} + +void DeclaratorCompiler::visitParameterDeclaration(ParameterDeclarationAST *node) +{ + Parameter p; + + TypeCompiler type_cc(_M_binder); + DeclaratorCompiler decl_cc(_M_binder); + + decl_cc.run(node->declarator); + + p.name = decl_cc.id(); + p.type = CompilerUtils::typeDescription(node->type_specifier, node->declarator, _M_binder); + if (node->expression != 0) { + const Token &start = _M_token_stream->token((int) node->expression->start_token); + const Token &end = _M_token_stream->token((int) node->expression->end_token); + int length = (int)(end.position - start.position); + + p.defaultValueExpression = QString(); + QString source = QString::fromUtf8(&start.text[start.position], length).trimmed(); + QStringList list = source.split("\n"); + + + for (int i = 0; i < list.size(); ++i) { + if (!list.at(i).startsWith("#")) + p.defaultValueExpression += list.at(i).trimmed(); + } + + p.defaultValue = p.defaultValueExpression.size() > 0; + + } + + _M_parameters.append(p); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/declarator_compiler.h b/parser/declarator_compiler.h new file mode 100644 index 000000000..70a65b058 --- /dev/null +++ b/parser/declarator_compiler.h @@ -0,0 +1,96 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef DECLARATOR_COMPILER_H +#define DECLARATOR_COMPILER_H + +#include "default_visitor.h" +#include "codemodel.h" + +#include +#include + +class TokenStream; +class Binder; + +class DeclaratorCompiler: protected DefaultVisitor +{ +public: + struct Parameter { + TypeInfo type; + QString name; + QString defaultValueExpression; + bool defaultValue; + + Parameter(): defaultValue(false) {} + }; + +public: + DeclaratorCompiler(Binder *binder); + + void run(DeclaratorAST *node); + + inline QString id() const { + return _M_id; + } + inline QStringList arrayElements() const { + return _M_array; + } + inline bool isFunction() const { + return _M_function; + } + inline bool isVariadics() const { + return _M_variadics; + } + inline bool isReference() const { + return _M_reference; + } + inline int indirection() const { + return _M_indirection; + } + inline QList parameters() const { + return _M_parameters; + } + +protected: + virtual void visitPtrOperator(PtrOperatorAST *node); + virtual void visitParameterDeclaration(ParameterDeclarationAST *node); + +private: + Binder *_M_binder; + TokenStream *_M_token_stream; + + bool _M_function; + bool _M_reference; + bool _M_variadics; + int _M_indirection; + QString _M_id; + QStringList _M_array; + QList _M_parameters; +}; + +#endif // DECLARATOR_COMPILER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/default_visitor.cpp b/parser/default_visitor.cpp new file mode 100644 index 000000000..07ab968cc --- /dev/null +++ b/parser/default_visitor.cpp @@ -0,0 +1,459 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "default_visitor.h" + +void DefaultVisitor::visitAccessSpecifier(AccessSpecifierAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitAsmDefinition(AsmDefinitionAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitBaseClause(BaseClauseAST *node) +{ + visitNodes(this, node->base_specifiers); +} + +void DefaultVisitor::visitBaseSpecifier(BaseSpecifierAST *node) +{ + visit(node->name); +} + +void DefaultVisitor::visitBinaryExpression(BinaryExpressionAST *node) +{ + visit(node->left_expression); + visit(node->right_expression); +} + +void DefaultVisitor::visitCastExpression(CastExpressionAST *node) +{ + visit(node->type_id); + visit(node->expression); +} + +void DefaultVisitor::visitClassMemberAccess(ClassMemberAccessAST *node) +{ + visit(node->name); +} + +void DefaultVisitor::visitClassSpecifier(ClassSpecifierAST *node) +{ + visit(node->win_decl_specifiers); + visit(node->name); + visit(node->base_clause); + visitNodes(this, node->member_specs); +} + +void DefaultVisitor::visitCompoundStatement(CompoundStatementAST *node) +{ + visitNodes(this, node->statements); +} + +void DefaultVisitor::visitCondition(ConditionAST *node) +{ + visit(node->type_specifier); + visit(node->declarator); + visit(node->expression); +} + +void DefaultVisitor::visitConditionalExpression(ConditionalExpressionAST *node) +{ + visit(node->condition); + visit(node->left_expression); + visit(node->right_expression); +} + +void DefaultVisitor::visitCppCastExpression(CppCastExpressionAST *node) +{ + visit(node->type_id); + visit(node->expression); + visitNodes(this, node->sub_expressions); +} + +void DefaultVisitor::visitCtorInitializer(CtorInitializerAST *node) +{ + visitNodes(this, node->member_initializers); +} + +void DefaultVisitor::visitDeclarationStatement(DeclarationStatementAST *node) +{ + visit(node->declaration); +} + +void DefaultVisitor::visitDeclarator(DeclaratorAST *node) +{ + visit(node->sub_declarator); + visitNodes(this, node->ptr_ops); + visit(node->id); + visit(node->bit_expression); + visitNodes(this, node->array_dimensions); + visit(node->parameter_declaration_clause); + visit(node->exception_spec); +} + +void DefaultVisitor::visitDeleteExpression(DeleteExpressionAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitDoStatement(DoStatementAST *node) +{ + visit(node->statement); + visit(node->expression); +} + +void DefaultVisitor::visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *node) +{ + visit(node->name); +} + +void DefaultVisitor::visitEnumSpecifier(EnumSpecifierAST *node) +{ + visit(node->name); + visitNodes(this, node->enumerators); +} + +void DefaultVisitor::visitEnumerator(EnumeratorAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitExceptionSpecification(ExceptionSpecificationAST *node) +{ + visitNodes(this, node->type_ids); +} + +void DefaultVisitor::visitExpressionOrDeclarationStatement(ExpressionOrDeclarationStatementAST *node) +{ + visit(node->expression); + visit(node->declaration); +} + +void DefaultVisitor::visitExpressionStatement(ExpressionStatementAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitForStatement(ForStatementAST *node) +{ + visit(node->init_statement); + visit(node->condition); + visit(node->expression); + visit(node->statement); +} + +void DefaultVisitor::visitFunctionCall(FunctionCallAST *node) +{ + visit(node->arguments); +} + +void DefaultVisitor::visitFunctionDefinition(FunctionDefinitionAST *node) +{ + visit(node->type_specifier); + visit(node->init_declarator); + visit(node->function_body); + visit(node->win_decl_specifiers); +} + +void DefaultVisitor::visitIfStatement(IfStatementAST *node) +{ + visit(node->condition); + visit(node->statement); + visit(node->else_statement); +} + +void DefaultVisitor::visitIncrDecrExpression(IncrDecrExpressionAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitInitDeclarator(InitDeclaratorAST *node) +{ + visit(node->declarator); + visit(node->initializer); +} + +void DefaultVisitor::visitInitializer(InitializerAST *node) +{ + visit(node->initializer_clause); + visit(node->expression); +} + +void DefaultVisitor::visitInitializerClause(InitializerClauseAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitLabeledStatement(LabeledStatementAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitLinkageBody(LinkageBodyAST *node) +{ + visitNodes(this, node->declarations); +} + +void DefaultVisitor::visitLinkageSpecification(LinkageSpecificationAST *node) +{ + visit(node->linkage_body); + visit(node->declaration); +} + +void DefaultVisitor::visitMemInitializer(MemInitializerAST *node) +{ + visit(node->initializer_id); + visit(node->expression); +} + +void DefaultVisitor::visitName(NameAST *node) +{ + visitNodes(this, node->qualified_names); + visit(node->unqualified_name); +} + +void DefaultVisitor::visitNamespace(NamespaceAST *node) +{ + visit(node->linkage_body); +} + +void DefaultVisitor::visitNamespaceAliasDefinition(NamespaceAliasDefinitionAST *node) +{ + visit(node->alias_name); +} + +void DefaultVisitor::visitNewDeclarator(NewDeclaratorAST *node) +{ + visit(node->ptr_op); + visit(node->sub_declarator); + visitNodes(this, node->expressions); +} + +void DefaultVisitor::visitNewExpression(NewExpressionAST *node) +{ + visit(node->expression); + visit(node->type_id); + visit(node->new_type_id); + visit(node->new_initializer); +} + +void DefaultVisitor::visitNewInitializer(NewInitializerAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitNewTypeId(NewTypeIdAST *node) +{ + visit(node->type_specifier); + visit(node->new_initializer); + visit(node->new_declarator); +} + +void DefaultVisitor::visitOperator(OperatorAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitOperatorFunctionId(OperatorFunctionIdAST *node) +{ + visit(node->op); + visit(node->type_specifier); + visitNodes(this, node->ptr_ops); +} + +void DefaultVisitor::visitParameterDeclaration(ParameterDeclarationAST *node) +{ + visit(node->type_specifier); + visit(node->declarator); + visit(node->expression); +} + +void DefaultVisitor::visitParameterDeclarationClause(ParameterDeclarationClauseAST *node) +{ + visitNodes(this, node->parameter_declarations); +} + +void DefaultVisitor::visitPostfixExpression(PostfixExpressionAST *node) +{ + visit(node->type_specifier); + visit(node->expression); + visitNodes(this, node->sub_expressions); +} + +void DefaultVisitor::visitPrimaryExpression(PrimaryExpressionAST *node) +{ + visit(node->literal); + visit(node->expression_statement); + visit(node->sub_expression); + visit(node->name); +} + +void DefaultVisitor::visitPtrOperator(PtrOperatorAST *node) +{ + visit(node->mem_ptr); +} + +void DefaultVisitor::visitPtrToMember(PtrToMemberAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitReturnStatement(ReturnStatementAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitSimpleDeclaration(SimpleDeclarationAST *node) +{ + visit(node->type_specifier); + visitNodes(this, node->init_declarators); + visit(node->win_decl_specifiers); +} + +void DefaultVisitor::visitSimpleTypeSpecifier(SimpleTypeSpecifierAST *node) +{ + visit(node->name); + visit(node->type_id); + visit(node->expression); +} + +void DefaultVisitor::visitSizeofExpression(SizeofExpressionAST *node) +{ + visit(node->type_id); + visit(node->expression); +} + +void DefaultVisitor::visitStringLiteral(StringLiteralAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitSubscriptExpression(SubscriptExpressionAST *node) +{ + visit(node->subscript); +} + +void DefaultVisitor::visitSwitchStatement(SwitchStatementAST *node) +{ + visit(node->condition); + visit(node->statement); +} + +void DefaultVisitor::visitTemplateArgument(TemplateArgumentAST *node) +{ + visit(node->type_id); + visit(node->expression); +} + +void DefaultVisitor::visitTemplateDeclaration(TemplateDeclarationAST *node) +{ + visitNodes(this, node->template_parameters); + visit(node->declaration); +} + +void DefaultVisitor::visitTemplateParameter(TemplateParameterAST *node) +{ + visit(node->type_parameter); + visit(node->parameter_declaration); +} + +void DefaultVisitor::visitThrowExpression(ThrowExpressionAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitTranslationUnit(TranslationUnitAST *node) +{ + visitNodes(this, node->declarations); +} + +void DefaultVisitor::visitTryBlockStatement(TryBlockStatementAST *) +{ + // nothing to do +} + +void DefaultVisitor::visitTypeId(TypeIdAST *node) +{ + visit(node->type_specifier); + visit(node->declarator); +} + +void DefaultVisitor::visitTypeIdentification(TypeIdentificationAST *node) +{ + visit(node->name); + visit(node->expression); +} + +void DefaultVisitor::visitTypeParameter(TypeParameterAST *node) +{ + visit(node->name); + visit(node->type_id); + visitNodes(this, node->template_parameters); + visit(node->template_name); +} + +void DefaultVisitor::visitTypedef(TypedefAST *node) +{ + visit(node->type_specifier); + visitNodes(this, node->init_declarators); +} + +void DefaultVisitor::visitUnaryExpression(UnaryExpressionAST *node) +{ + visit(node->expression); +} + +void DefaultVisitor::visitUnqualifiedName(UnqualifiedNameAST *node) +{ + visit(node->operator_id); + visitNodes(this, node->template_arguments); +} + +void DefaultVisitor::visitUsing(UsingAST *node) +{ + visit(node->name); +} + +void DefaultVisitor::visitUsingDirective(UsingDirectiveAST *node) +{ + visit(node->name); +} + +void DefaultVisitor::visitWhileStatement(WhileStatementAST *node) +{ + visit(node->condition); + visit(node->statement); +} + +void DefaultVisitor::visitWinDeclSpec(WinDeclSpecAST *) +{ + // nothing to do +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/default_visitor.h b/parser/default_visitor.h new file mode 100644 index 000000000..ec1caf3bc --- /dev/null +++ b/parser/default_visitor.h @@ -0,0 +1,118 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef DEFAULT_VISITOR_H +#define DEFAULT_VISITOR_H + +#include "visitor.h" + +class DefaultVisitor: public Visitor +{ +public: + DefaultVisitor() {} + +protected: + virtual void visitAccessSpecifier(AccessSpecifierAST *); + virtual void visitAsmDefinition(AsmDefinitionAST *); + virtual void visitBaseClause(BaseClauseAST *); + virtual void visitBaseSpecifier(BaseSpecifierAST *); + virtual void visitBinaryExpression(BinaryExpressionAST *); + virtual void visitCastExpression(CastExpressionAST *); + virtual void visitClassMemberAccess(ClassMemberAccessAST *); + virtual void visitClassSpecifier(ClassSpecifierAST *); + virtual void visitCompoundStatement(CompoundStatementAST *); + virtual void visitCondition(ConditionAST *); + virtual void visitConditionalExpression(ConditionalExpressionAST *); + virtual void visitCppCastExpression(CppCastExpressionAST *); + virtual void visitCtorInitializer(CtorInitializerAST *); + virtual void visitDeclarationStatement(DeclarationStatementAST *); + virtual void visitDeclarator(DeclaratorAST *); + virtual void visitDeleteExpression(DeleteExpressionAST *); + virtual void visitDoStatement(DoStatementAST *); + virtual void visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *); + virtual void visitEnumSpecifier(EnumSpecifierAST *); + virtual void visitEnumerator(EnumeratorAST *); + virtual void visitExceptionSpecification(ExceptionSpecificationAST *); + virtual void visitExpressionOrDeclarationStatement(ExpressionOrDeclarationStatementAST *); + virtual void visitExpressionStatement(ExpressionStatementAST *); + virtual void visitForStatement(ForStatementAST *); + virtual void visitFunctionCall(FunctionCallAST *); + virtual void visitFunctionDefinition(FunctionDefinitionAST *); + virtual void visitIfStatement(IfStatementAST *); + virtual void visitIncrDecrExpression(IncrDecrExpressionAST *); + virtual void visitInitDeclarator(InitDeclaratorAST *); + virtual void visitInitializer(InitializerAST *); + virtual void visitInitializerClause(InitializerClauseAST *); + virtual void visitLabeledStatement(LabeledStatementAST *); + virtual void visitLinkageBody(LinkageBodyAST *); + virtual void visitLinkageSpecification(LinkageSpecificationAST *); + virtual void visitMemInitializer(MemInitializerAST *); + virtual void visitName(NameAST *); + virtual void visitNamespace(NamespaceAST *); + virtual void visitNamespaceAliasDefinition(NamespaceAliasDefinitionAST *); + virtual void visitNewDeclarator(NewDeclaratorAST *); + virtual void visitNewExpression(NewExpressionAST *); + virtual void visitNewInitializer(NewInitializerAST *); + virtual void visitNewTypeId(NewTypeIdAST *); + virtual void visitOperator(OperatorAST *); + virtual void visitOperatorFunctionId(OperatorFunctionIdAST *); + virtual void visitParameterDeclaration(ParameterDeclarationAST *); + virtual void visitParameterDeclarationClause(ParameterDeclarationClauseAST *); + virtual void visitPostfixExpression(PostfixExpressionAST *); + virtual void visitPrimaryExpression(PrimaryExpressionAST *); + virtual void visitPtrOperator(PtrOperatorAST *); + virtual void visitPtrToMember(PtrToMemberAST *); + virtual void visitReturnStatement(ReturnStatementAST *); + virtual void visitSimpleDeclaration(SimpleDeclarationAST *); + virtual void visitSimpleTypeSpecifier(SimpleTypeSpecifierAST *); + virtual void visitSizeofExpression(SizeofExpressionAST *); + virtual void visitStringLiteral(StringLiteralAST *); + virtual void visitSubscriptExpression(SubscriptExpressionAST *); + virtual void visitSwitchStatement(SwitchStatementAST *); + virtual void visitTemplateArgument(TemplateArgumentAST *); + virtual void visitTemplateDeclaration(TemplateDeclarationAST *); + virtual void visitTemplateParameter(TemplateParameterAST *); + virtual void visitThrowExpression(ThrowExpressionAST *); + virtual void visitTranslationUnit(TranslationUnitAST *); + virtual void visitTryBlockStatement(TryBlockStatementAST *); + virtual void visitTypeId(TypeIdAST *); + virtual void visitTypeIdentification(TypeIdentificationAST *); + virtual void visitTypeParameter(TypeParameterAST *); + virtual void visitTypedef(TypedefAST *); + virtual void visitUnaryExpression(UnaryExpressionAST *); + virtual void visitUnqualifiedName(UnqualifiedNameAST *); + virtual void visitUsing(UsingAST *); + virtual void visitUsingDirective(UsingDirectiveAST *); + virtual void visitWhileStatement(WhileStatementAST *); + virtual void visitWinDeclSpec(WinDeclSpecAST *); + +private: + typedef void (Visitor::*visitor_fun_ptr)(AST *); + static visitor_fun_ptr _S_table[]; +}; + +#endif // VISITOR_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/dumptree.cpp b/parser/dumptree.cpp new file mode 100644 index 000000000..1514b0f64 --- /dev/null +++ b/parser/dumptree.cpp @@ -0,0 +1,125 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "dumptree.h" + +#include +#include + +static char const * const names[] = { + 0, + "AccessSpecifier", + "AsmDefinition", + "BaseClause", + "BaseSpecifier", + "BinaryExpression", + "CastExpression", + "ClassMemberAccess", + "ClassSpecifier", + "CompoundStatement", + "Condition", + "ConditionalExpression", + "CppCastExpression", + "CtorInitializer", + "DeclarationStatement", + "Declarator", + "DeleteExpression", + "DoStatement", + "ElaboratedTypeSpecifier", + "EnumSpecifier", + "Enumerator", + "ExceptionSpecification", + "ExpressionOrDeclarationStatement", + "ExpressionStatement", + "ForStatement", + "FunctionCall", + "FunctionDefinition", + "IfStatement", + "IncrDecrExpression", + "InitDeclarator", + "Initializer", + "InitializerClause", + "LabeledStatement", + "LinkageBody", + "LinkageSpecification", + "MemInitializer", + "Name", + "Namespace", + "NamespaceAliasDefinition", + "NewDeclarator", + "NewExpression", + "NewInitializer", + "NewTypeId", + "Operator", + "OperatorFunctionId", + "ParameterDeclaration", + "ParameterDeclarationClause", + "PostfixExpression", + "PrimaryExpression", + "PtrOperator", + "PtrToMember", + "ReturnStatement", + "SimpleDeclaration", + "SimpleTypeSpecifier", + "SizeofExpression", + "StringLiteral", + "SubscriptExpression", + "SwitchStatement", + "TemplateArgument", + "TemplateDeclaration", + "TemplateParameter", + "ThrowExpression", + "TranslationUnit", + "TryBlockStatement", + "TypeId", + "TypeIdentification", + "TypeParameter", + "Typedef", + "UnaryExpression", + "UnqualifiedName", + "Using", + "UsingDirective", + "WhileStatement", + "WinDeclSpec" +}; + +DumpTree::DumpTree() +{ +} + +void DumpTree::visit(AST *node) +{ + static int indent = 0; + + if (node) + qDebug() << QString(indent * 2, ' ').toLatin1().constData() << names[node->kind] + << '[' << node->start_token << ", " << node->end_token << ']'; + + ++indent; + DefaultVisitor::visit(node); + --indent; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/dumptree.h b/parser/dumptree.h new file mode 100644 index 000000000..cfc55e2e6 --- /dev/null +++ b/parser/dumptree.h @@ -0,0 +1,46 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef DUMPTREE_H +#define DUMPTREE_H + +#include "default_visitor.h" + +class DumpTree: protected DefaultVisitor +{ +public: + DumpTree(); + + void dump(AST *node) { + visit(node); + } + +protected: + virtual void visit(AST *node); +}; + +#endif // DUMPTREE_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/lexer.cpp b/parser/lexer.cpp new file mode 100644 index 000000000..2abb540d0 --- /dev/null +++ b/parser/lexer.cpp @@ -0,0 +1,1695 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "lexer.h" +#include "tokens.h" +#include "control.h" + +#include +#include + +scan_fun_ptr Lexer::s_scan_keyword_table[] = { + &Lexer::scanKeyword0, &Lexer::scanKeyword0, + &Lexer::scanKeyword2, &Lexer::scanKeyword3, + &Lexer::scanKeyword4, &Lexer::scanKeyword5, + &Lexer::scanKeyword6, &Lexer::scanKeyword7, + &Lexer::scanKeyword8, &Lexer::scanKeyword9, + &Lexer::scanKeyword10, &Lexer::scanKeyword11, + &Lexer::scanKeyword12, &Lexer::scanKeyword13, + &Lexer::scanKeyword14, &Lexer::scanKeyword0, + &Lexer::scanKeyword16 +}; + +void LocationManager::extract_line(int offset, int *line, QString *filename) const +{ + *line = 0; + if (token_stream.size() < 1) + return; + + const unsigned char *begin_buffer = reinterpret_cast(token_stream[0].text); + const unsigned char *cursor = begin_buffer + offset; + + ++cursor; // skip '#' + if (std::isspace(*cursor) && std::isdigit(*(cursor + 1))) { + ++cursor; + char buffer[1024], *cp = buffer; + do { + *cp++ = *cursor++; + } while (std::isdigit(*cursor)); + *cp = '\0'; + int l = strtol(buffer, 0, 0); + + Q_ASSERT(std::isspace(*cursor)); + ++cursor; + + Q_ASSERT(*cursor == '"'); + ++cursor; + + cp = buffer; + while (*cursor && *cursor != '"') { + *cp++ = *cursor++; + } + *cp = '\0'; + Q_ASSERT(*cursor == '"'); + ++cursor; + + *filename = buffer; + *line = l; + // printf("filename: %s line: %d\n", buffer, line); + } +} + +void LocationManager::positionAt(std::size_t offset, int *line, int *column, + QString *filename) const +{ + int ppline, ppcolumn; + line_table.positionAt(offset, &ppline, &ppcolumn); + + int base_line; + extract_line((int) line_table[ppline-1], &base_line, filename); + + int line2, column2; + location_table.positionAt((int) line_table[ppline-1], &line2, &column2); + + location_table.positionAt(offset, line, column); + *line = base_line + *line - line2 - 1; +} + +scan_fun_ptr Lexer::s_scan_table[256]; +bool Lexer::s_initialized = false; + +void Lexer::tokenize(const char *contents, std::size_t size) +{ + if (!s_initialized) + initialize_scan_table(); + + token_stream.resize(1024); + token_stream[0].kind = Token_EOF; + token_stream[0].text = contents; + + index = 1; + + cursor = (const unsigned char *) contents; + begin_buffer = (const unsigned char *) contents; + end_buffer = cursor + size; + + location_table.resize(1024); + location_table[0] = 0; + location_table.current_line = 1; + + line_table.resize(1024); + line_table[0] = 0; + line_table.current_line = 1; + + do { + if (index == token_stream.size()) + token_stream.resize(token_stream.size() * 2); + + Token *current_token = &token_stream[(int) index]; + current_token->text = reinterpret_cast(begin_buffer); + current_token->position = cursor - begin_buffer; + (this->*s_scan_table[*cursor])(); + current_token->size = cursor - begin_buffer - current_token->position; + } while (cursor < end_buffer); + + if (index == token_stream.size()) + token_stream.resize(token_stream.size() * 2); + + Q_ASSERT(index < token_stream.size()); + token_stream[(int) index].position = cursor - begin_buffer; + token_stream[(int) index].kind = Token_EOF; +} + +void Lexer::reportError(const QString& msg) +{ + int line, column; + QString fileName; + + std::size_t tok = token_stream.cursor(); + _M_location.positionAt(token_stream.position(tok), + &line, &column, &fileName); + + Control::ErrorMessage errmsg; + errmsg.setLine(line + 1); + errmsg.setColumn(column); + errmsg.setFileName(fileName); + errmsg.setMessage(QLatin1String("** LEXER ERROR ") + msg); + control->reportError(errmsg); +} + +void Lexer::initialize_scan_table() +{ + s_initialized = true; + + for (int i = 0; i < 256; ++i) { + if (isspace(i)) + s_scan_table[i] = &Lexer::scan_white_spaces; + else if (isalpha(i) || i == '_') + s_scan_table[i] = &Lexer::scan_identifier_or_keyword; + else if (isdigit(i)) + s_scan_table[i] = &Lexer::scan_int_constant; + else + s_scan_table[i] = &Lexer::scan_invalid_input; + } + + s_scan_table[int('L')] = &Lexer::scan_identifier_or_literal; + s_scan_table[int('\n')] = &Lexer::scan_newline; + s_scan_table[int('#')] = &Lexer::scan_preprocessor; + + s_scan_table[int('\'')] = &Lexer::scan_char_constant; + s_scan_table[int('"')] = &Lexer::scan_string_constant; + + s_scan_table[int('.')] = &Lexer::scan_int_constant; + + s_scan_table[int('!')] = &Lexer::scan_not; + s_scan_table[int('%')] = &Lexer::scan_remainder; + s_scan_table[int('&')] = &Lexer::scan_and; + s_scan_table[int('(')] = &Lexer::scan_left_paren; + s_scan_table[int(')')] = &Lexer::scan_right_paren; + s_scan_table[int('*')] = &Lexer::scan_star; + s_scan_table[int('+')] = &Lexer::scan_plus; + s_scan_table[int(',')] = &Lexer::scan_comma; + s_scan_table[int('-')] = &Lexer::scan_minus; + s_scan_table[int('/')] = &Lexer::scan_divide; + s_scan_table[int(':')] = &Lexer::scan_colon; + s_scan_table[int(';')] = &Lexer::scan_semicolon; + s_scan_table[int('<')] = &Lexer::scan_less; + s_scan_table[int('=')] = &Lexer::scan_equal; + s_scan_table[int('>')] = &Lexer::scan_greater; + s_scan_table[int('?')] = &Lexer::scan_question; + s_scan_table[int('[')] = &Lexer::scan_left_bracket; + s_scan_table[int(']')] = &Lexer::scan_right_bracket; + s_scan_table[int('^')] = &Lexer::scan_xor; + s_scan_table[int('{')] = &Lexer::scan_left_brace; + s_scan_table[int('|')] = &Lexer::scan_or; + s_scan_table[int('}')] = &Lexer::scan_right_brace; + s_scan_table[int('~')] = &Lexer::scan_tilde; + + s_scan_table[0] = &Lexer::scan_EOF; +} + +void Lexer::scan_preprocessor() +{ + if (line_table.current_line == line_table.size()) + line_table.resize(line_table.current_line * 2); + + line_table[(int) line_table.current_line++] = (cursor - begin_buffer); + + while (*cursor && *cursor != '\n') + ++cursor; + + if (*cursor != '\n') + reportError("expected newline"); +} + +void Lexer::scan_char_constant() +{ + const unsigned char *begin = cursor; + + ++cursor; + while (*cursor && *cursor != '\'') { + if (*cursor == '\n') + reportError("did not expect newline"); + + if (*cursor == '\\') + ++cursor; + ++cursor; + } + + if (*cursor != '\'') + reportError("expected \'"); + + ++cursor; + + token_stream[(int) index].extra.symbol = + control->findOrInsertName((const char*) begin, cursor - begin); + + token_stream[(int) index++].kind = Token_char_literal; +} + +void Lexer::scan_string_constant() +{ + const unsigned char *begin = cursor; + + ++cursor; + while (*cursor && *cursor != '"') { + if (*cursor == '\n') + reportError("did not expect newline"); + + if (*cursor == '\\') + ++cursor; + ++cursor; + } + + if (*cursor != '"') + reportError("expected \""); + + ++cursor; + + token_stream[(int) index].extra.symbol = + control->findOrInsertName((const char*) begin, cursor - begin); + + token_stream[(int) index++].kind = Token_string_literal; +} + +void Lexer::scan_newline() +{ + if (location_table.current_line == location_table.size()) + location_table.resize(location_table.current_line * 2); + + location_table[(int) location_table.current_line++] = (cursor - begin_buffer); + ++cursor; +} + +void Lexer::scan_white_spaces() +{ + while (isspace(*cursor)) { + if (*cursor == '\n') + scan_newline(); + else + ++cursor; + } +} + +void Lexer::scan_identifier_or_literal() +{ + switch (*(cursor + 1)) { + case '\'': + ++cursor; + scan_char_constant(); + break; + + case '\"': + ++cursor; + scan_string_constant(); + break; + + default: + scan_identifier_or_keyword(); + break; + } +} + +void Lexer::scan_identifier_or_keyword() +{ + const unsigned char *skip = cursor; + while (isalnum(*skip) || *skip == '_') + ++skip; + + int n = skip - cursor; + Token *current_token = &token_stream[(int) index]; + (this->*s_scan_keyword_table[n < 17 ? n : 0])(); + + if (current_token->kind == Token_identifier) { + current_token->extra.symbol = + control->findOrInsertName((const char*) cursor, n); + } + + cursor = skip; +} + +void Lexer::scan_int_constant() +{ + if (*cursor == '.' && !std::isdigit(*(cursor + 1))) { + scan_dot(); + return; + } + + const unsigned char *begin = cursor; + + while (isalnum(*cursor) || *cursor == '.') + ++cursor; + + token_stream[(int) index].extra.symbol = + control->findOrInsertName((const char*) begin, cursor - begin); + + token_stream[(int) index++].kind = Token_number_literal; +} + +void Lexer::scan_not() +{ + /* + '!' ::= not + '!=' ::= not_equal + */ + + ++cursor; + + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_not_eq; + } else { + token_stream[(int) index++].kind = '!'; + } +} + +void Lexer::scan_remainder() +{ + /* + '%' ::= remainder + '%=' ::= remainder_equal + */ + + ++cursor; + + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else { + token_stream[(int) index++].kind = '%'; + } +} + +void Lexer::scan_and() +{ + /* + '&&' ::= and_and + '&' ::= and + '&=' ::= and_equal + */ + + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else if (*cursor == '&') { + ++cursor; + token_stream[(int) index++].kind = Token_and; + } else { + token_stream[(int) index++].kind = '&'; + } +} + +void Lexer::scan_left_paren() +{ + ++cursor; + token_stream[(int) index++].kind = '('; +} + +void Lexer::scan_right_paren() +{ + ++cursor; + token_stream[(int) index++].kind = ')'; +} + +void Lexer::scan_star() +{ + /* + '*' ::= star + '*=' ::= star_equal + */ + + ++cursor; + + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else { + token_stream[(int) index++].kind = '*'; + } +} + +void Lexer::scan_plus() +{ + /* + '+' ::= plus + '++' ::= incr + '+=' ::= plus_equal + */ + + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else if (*cursor == '+') { + ++cursor; + token_stream[(int) index++].kind = Token_incr; + } else { + token_stream[(int) index++].kind = '+'; + } +} + +void Lexer::scan_comma() +{ + ++cursor; + token_stream[(int) index++].kind = ','; +} + +void Lexer::scan_minus() +{ + /* + '-' ::= minus + '--' ::= decr + '-=' ::= minus_equal + '->' ::= left_arrow + */ + + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else if (*cursor == '-') { + ++cursor; + token_stream[(int) index++].kind = Token_decr; + } else if (*cursor == '>') { + ++cursor; + token_stream[(int) index++].kind = Token_arrow; + if (*cursor == '*') { + ++cursor; + token_stream[(int) index++].kind = Token_ptrmem; + } + } else { + token_stream[(int) index++].kind = '-'; + } +} + +void Lexer::scan_dot() +{ + /* + '.' ::= dot + '...' ::= ellipsis + */ + + ++cursor; + if (*cursor == '.' && *(cursor + 1) == '.') { + cursor += 2; + token_stream[(int) index++].kind = Token_ellipsis; + } else if (*cursor == '.' && *(cursor + 1) == '*') { + cursor += 2; + token_stream[(int) index++].kind = Token_ptrmem; + } else + token_stream[(int) index++].kind = '.'; +} + +void Lexer::scan_divide() +{ + /* + '/' ::= divide + '/=' ::= divide_equal + */ + + ++cursor; + + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else { + token_stream[(int) index++].kind = '/'; + } +} + +void Lexer::scan_colon() +{ + ++cursor; + if (*cursor == ':') { + ++cursor; + token_stream[(int) index++].kind = Token_scope; + } else { + token_stream[(int) index++].kind = ':'; + } +} + +void Lexer::scan_semicolon() +{ + ++cursor; + token_stream[(int) index++].kind = ';'; +} + +void Lexer::scan_less() +{ + /* + '<' ::= less + '<<' ::= left_shift + '<<=' ::= left_shift_equal + '<=' ::= less_equal + */ + + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_leq; + } else if (*cursor == '<') { + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else { + token_stream[(int) index++].kind = Token_shift; + } + } else { + token_stream[(int) index++].kind = '<'; + } +} + +void Lexer::scan_equal() +{ + /* + '=' ::= equal + '==' ::= equal_equal + */ + ++cursor; + + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_eq; + } else { + token_stream[(int) index++].kind = '='; + } +} + +void Lexer::scan_greater() +{ + /* + '>' ::= greater + '>=' ::= greater_equal + '>>' ::= right_shift + '>>=' ::= right_shift_equal + */ + + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_geq; + } else if (*cursor == '>') { + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else { + token_stream[(int) index++].kind = Token_shift; + } + } else { + token_stream[(int) index++].kind = '>'; + } +} + +void Lexer::scan_question() +{ + ++cursor; + token_stream[(int) index++].kind = '?'; +} + +void Lexer::scan_left_bracket() +{ + ++cursor; + token_stream[(int) index++].kind = '['; +} + +void Lexer::scan_right_bracket() +{ + ++cursor; + token_stream[(int) index++].kind = ']'; +} + +void Lexer::scan_xor() +{ + /* + '^' ::= xor + '^=' ::= xor_equal + */ + ++cursor; + + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else { + token_stream[(int) index++].kind = '^'; + } +} + +void Lexer::scan_left_brace() +{ + ++cursor; + token_stream[(int) index++].kind = '{'; +} + +void Lexer::scan_or() +{ + /* + '|' ::= or + '|=' ::= or_equal + '||' ::= or_or + */ + ++cursor; + if (*cursor == '=') { + ++cursor; + token_stream[(int) index++].kind = Token_assign; + } else if (*cursor == '|') { + ++cursor; + token_stream[(int) index++].kind = Token_or; + } else { + token_stream[(int) index++].kind = '|'; + } +} + +void Lexer::scan_right_brace() +{ + ++cursor; + token_stream[(int) index++].kind = '}'; +} + +void Lexer::scan_tilde() +{ + ++cursor; + token_stream[(int) index++].kind = '~'; +} + +void Lexer::scan_EOF() +{ + ++cursor; + token_stream[(int) index++].kind = Token_EOF; +} + +void Lexer::scan_invalid_input() +{ + QString errmsg("invalid input: %1"); + errmsg.arg(int(*cursor)); + reportError(errmsg); + ++cursor; +} + +void LocationTable::positionAt(std::size_t offset, int max_line, + int *line, int *column) const +{ + if (!(line && column && max_line != 0)) + return; + + int first = 0; + int len = max_line; + int half; + int middle; + + while (len > 0) { + half = len >> 1; + middle = first; + + middle += half; + + if (lines[middle] < offset) { + first = middle; + ++first; + len = len - half - 1; + } else + len = half; + } + + *line = std::max(first, 1); + *column = (int)(offset - lines[*line - 1] - 1); + + if (*column < 0) { + *column = 0; + } +} + +void Lexer::scanKeyword0() +{ + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword2() +{ + switch (*cursor) { + case 'i': + if (*(cursor + 1) == 'f') { + token_stream[(int) index++].kind = Token_if; + return; + } + break; + + case 'd': + if (*(cursor + 1) == 'o') { + token_stream[(int) index++].kind = Token_do; + return; + } + break; + + case 'o': + if (*(cursor + 1) == 'r') { + token_stream[(int) index++].kind = Token_or; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword3() +{ + switch (*cursor) { + case 'a': + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 'd') { + token_stream[(int) index++].kind = Token_and; + return; + } + if (*(cursor + 1) == 's' && + *(cursor + 2) == 'm') { + token_stream[(int) index++].kind = Token_asm; + return; + } + break; + + case 'f': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'r') { + token_stream[(int) index++].kind = Token_for; + return; + } + break; + + case 'i': + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 't') { + token_stream[(int) index++].kind = Token_int; + return; + } + break; + + case 'n': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 'w') { + token_stream[(int) index++].kind = Token_new; + return; + } + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 't') { + token_stream[(int) index++].kind = Token_not; + return; + } + break; + + case 't': + if (*(cursor + 1) == 'r' && + *(cursor + 2) == 'y') { + token_stream[(int) index++].kind = Token_try; + return; + } + break; + + case 'x': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'r') { + token_stream[(int) index++].kind = Token_xor; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword4() +{ + switch (*cursor) { + case 'a': + if (*(cursor + 1) == 'u' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'o') { + token_stream[(int) index++].kind = Token_auto; + return; + } + break; + + case 'c': + if (*(cursor + 1) == 'a' && + *(cursor + 2) == 's' && + *(cursor + 3) == 'e') { + token_stream[(int) index++].kind = Token_case; + return; + } + if (*(cursor + 1) == 'h' && + *(cursor + 2) == 'a' && + *(cursor + 3) == 'r') { + token_stream[(int) index++].kind = Token_char; + return; + } + break; + + case 'b': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'o' && + *(cursor + 3) == 'l') { + token_stream[(int) index++].kind = Token_bool; + return; + } + break; + + case 'e': + if (*(cursor + 1) == 'l' && + *(cursor + 2) == 's' && + *(cursor + 3) == 'e') { + token_stream[(int) index++].kind = Token_else; + return; + } + if (*(cursor + 1) == 'm' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 't') { + token_stream[(int) index++].kind = Token_emit; + return; + } + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 'u' && + *(cursor + 3) == 'm') { + token_stream[(int) index++].kind = Token_enum; + return; + } + break; + + case 'g': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'o') { + token_stream[(int) index++].kind = Token_goto; + return; + } + break; + + case 'l': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'n' && + *(cursor + 3) == 'g') { + token_stream[(int) index++].kind = Token_long; + return; + } + break; + + case 't': + if (*(cursor + 1) == 'h' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 's') { + token_stream[(int) index++].kind = Token_this; + return; + } + break; + + case 'v': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'd') { + token_stream[(int) index++].kind = Token_void; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword5() +{ + switch (*cursor) { + case 'c': + if (*(cursor + 1) == 'a' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'c' && + *(cursor + 4) == 'h') { + token_stream[(int) index++].kind = Token_catch; + return; + } + if (*(cursor + 1) == 'l' && + *(cursor + 2) == 'a' && + *(cursor + 3) == 's' && + *(cursor + 4) == 's') { + token_stream[(int) index++].kind = Token_class; + return; + } + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'm' && + *(cursor + 3) == 'p' && + *(cursor + 4) == 'l') { + token_stream[(int) index++].kind = Token_compl; + return; + } + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'n' && + *(cursor + 3) == 's' && + *(cursor + 4) == 't') { + token_stream[(int) index++].kind = Token_const; + return; + } + break; + + case 'b': + if (*(cursor + 1) == 'i' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'o' && + *(cursor + 4) == 'r') { + token_stream[(int) index++].kind = Token_bitor; + return; + } + if (*(cursor + 1) == 'r' && + *(cursor + 2) == 'e' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 'k') { + token_stream[(int) index++].kind = Token_break; + return; + } + break; + + case 'f': + if (*(cursor + 1) == 'l' && + *(cursor + 2) == 'o' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 't') { + token_stream[(int) index++].kind = Token_float; + return; + } + break; + + case 'o': + if (*(cursor + 1) == 'r' && + *(cursor + 2) == '_' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'q') { + token_stream[(int) index++].kind = Token_or_eq; + return; + } + break; + + case 's': + if (*(cursor + 1) == 'h' && + *(cursor + 2) == 'o' && + *(cursor + 3) == 'r' && + *(cursor + 4) == 't') { + token_stream[(int) index++].kind = Token_short; + return; + } + if (*(cursor + 1) == 'l' && + *(cursor + 2) == 'o' && + *(cursor + 3) == 't' && + *(cursor + 4) == 's') { + token_stream[(int) index++].kind = Token_slots; + return; + } + break; + + case 'u': + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'o' && + *(cursor + 4) == 'n') { + token_stream[(int) index++].kind = Token_union; + return; + } + if (*(cursor + 1) == 's' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'n' && + *(cursor + 4) == 'g') { + token_stream[(int) index++].kind = Token_using; + return; + } + break; + + case 't': + if (*(cursor + 1) == 'h' && + *(cursor + 2) == 'r' && + *(cursor + 3) == 'o' && + *(cursor + 4) == 'w') { + token_stream[(int) index++].kind = Token_throw; + return; + } + break; + + case 'w': + if (*(cursor + 1) == 'h' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'l' && + *(cursor + 4) == 'e') { + token_stream[(int) index++].kind = Token_while; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword6() +{ + switch (*cursor) { + case 'a': + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 'd' && + *(cursor + 3) == '_' && + *(cursor + 4) == 'e' && + *(cursor + 5) == 'q') { + token_stream[(int) index++].kind = Token_and_eq; + return; + } + break; + + case 'b': + if (*(cursor + 1) == 'i' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 'n' && + *(cursor + 5) == 'd') { + token_stream[(int) index++].kind = Token_bitand; + return; + } + break; + + case 'e': + if (*(cursor + 1) == 'x' && + *(cursor + 2) == 'p' && + *(cursor + 3) == 'o' && + *(cursor + 4) == 'r' && + *(cursor + 5) == 't') { + token_stream[(int) index++].kind = Token_export; + return; + } + if (*(cursor + 1) == 'x' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'r' && + *(cursor + 5) == 'n') { + token_stream[(int) index++].kind = Token_extern; + return; + } + break; + + case 'd': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 'l' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 't' && + *(cursor + 5) == 'e') { + token_stream[(int) index++].kind = Token_delete; + return; + } + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'u' && + *(cursor + 3) == 'b' && + *(cursor + 4) == 'l' && + *(cursor + 5) == 'e') { + token_stream[(int) index++].kind = Token_double; + return; + } + break; + + case 'f': + if (*(cursor + 1) == 'r' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'n' && + *(cursor + 5) == 'd') { + token_stream[(int) index++].kind = Token_friend; + return; + } + break; + + case 'i': + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 'l' && + *(cursor + 3) == 'i' && + *(cursor + 4) == 'n' && + *(cursor + 5) == 'e') { + token_stream[(int) index++].kind = Token_inline; + return; + } + break; + + case 'K': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'D' && + *(cursor + 3) == 'C' && + *(cursor + 4) == 'O' && + *(cursor + 5) == 'P') { + token_stream[(int) index++].kind = Token_K_DCOP; + return; + } + break; + + case 'n': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 't' && + *(cursor + 3) == '_' && + *(cursor + 4) == 'e' && + *(cursor + 5) == 'q') { + token_stream[(int) index++].kind = Token_not_eq; + return; + } + break; + + case 'p': + if (*(cursor + 1) == 'u' && + *(cursor + 2) == 'b' && + *(cursor + 3) == 'l' && + *(cursor + 4) == 'i' && + *(cursor + 5) == 'c') { + token_stream[(int) index++].kind = Token_public; + return; + } + break; + + case 's': + if (*(cursor + 1) == 'i' && + *(cursor + 2) == 'g' && + *(cursor + 3) == 'n' && + *(cursor + 4) == 'e' && + *(cursor + 5) == 'd') { + token_stream[(int) index++].kind = Token_signed; + return; + } + if (*(cursor + 1) == 'i' && + *(cursor + 2) == 'z' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'o' && + *(cursor + 5) == 'f') { + token_stream[(int) index++].kind = Token_sizeof; + return; + } + if (*(cursor + 1) == 't' && + *(cursor + 2) == 'a' && + *(cursor + 3) == 't' && + *(cursor + 4) == 'i' && + *(cursor + 5) == 'c') { + token_stream[(int) index++].kind = Token_static; + return; + } + if (*(cursor + 1) == 't' && + *(cursor + 2) == 'r' && + *(cursor + 3) == 'u' && + *(cursor + 4) == 'c' && + *(cursor + 5) == 't') { + token_stream[(int) index++].kind = Token_struct; + return; + } + if (*(cursor + 1) == 'w' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 't' && + *(cursor + 4) == 'c' && + *(cursor + 5) == 'h') { + token_stream[(int) index++].kind = Token_switch; + return; + } + break; + + case 'r': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'u' && + *(cursor + 4) == 'r' && + *(cursor + 5) == 'n') { + token_stream[(int) index++].kind = Token_return; + return; + } + break; + + case 't': + if (*(cursor + 1) == 'y' && + *(cursor + 2) == 'p' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'i' && + *(cursor + 5) == 'd') { + token_stream[(int) index++].kind = Token_typeid; + return; + } + break; + + case 'x': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'r' && + *(cursor + 3) == '_' && + *(cursor + 4) == 'e' && + *(cursor + 5) == 'q') { + token_stream[(int) index++].kind = Token_xor_eq; + return; + } + break; + + case 'k': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'd' && + *(cursor + 3) == 'c' && + *(cursor + 4) == 'o' && + *(cursor + 5) == 'p') { + token_stream[(int) index++].kind = Token_k_dcop; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword7() +{ + switch (*cursor) { + case 'd': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 'f' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 'u' && + *(cursor + 5) == 'l' && + *(cursor + 6) == 't') { + token_stream[(int) index++].kind = Token_default; + return; + } + break; + + case 'm': + if (*(cursor + 1) == 'u' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 'b' && + *(cursor + 5) == 'l' && + *(cursor + 6) == 'e') { + token_stream[(int) index++].kind = Token_mutable; + return; + } + break; + + case 'p': + if (*(cursor + 1) == 'r' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'v' && + *(cursor + 4) == 'a' && + *(cursor + 5) == 't' && + *(cursor + 6) == 'e') { + token_stream[(int) index++].kind = Token_private; + return; + } + break; + case 's': + if (*(cursor + 1) == 'i' && + *(cursor + 2) == 'g' && + *(cursor + 3) == 'n' && + *(cursor + 4) == 'a' && + *(cursor + 5) == 'l' && + *(cursor + 6) == 's') { + token_stream[(int) index++].kind = Token_signals; + return; + } + break; + case 't': + if (*(cursor + 1) == 'y' && + *(cursor + 2) == 'p' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'd' && + *(cursor + 5) == 'e' && + *(cursor + 6) == 'f') { + token_stream[(int) index++].kind = Token_typedef; + return; + } + break; + + case 'v': + if (*(cursor + 1) == 'i' && + *(cursor + 2) == 'r' && + *(cursor + 3) == 't' && + *(cursor + 4) == 'u' && + *(cursor + 5) == 'a' && + *(cursor + 6) == 'l') { + token_stream[(int) index++].kind = Token_virtual; + return; + } + break; + + case 'Q': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'E' && + *(cursor + 3) == 'N' && + *(cursor + 4) == 'U' && + *(cursor + 5) == 'M' && + *(cursor + 6) == 'S') { + token_stream[(int) index++].kind = Token_Q_ENUMS; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword8() +{ + switch (*cursor) { + case '_': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 't' && + *(cursor + 3) == 'y' && + *(cursor + 4) == 'p' && + *(cursor + 5) == 'e' && + *(cursor + 6) == 'o' && + *(cursor + 7) == 'f') { + token_stream[(int) index++].kind = Token___typeof; + return; + } + break; + + case 'c': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'n' && + *(cursor + 3) == 't' && + *(cursor + 4) == 'i' && + *(cursor + 5) == 'n' && + *(cursor + 6) == 'u' && + *(cursor + 7) == 'e') { + token_stream[(int) index++].kind = Token_continue; + return; + } + break; + + case 'e': + if (*(cursor + 1) == 'x' && + *(cursor + 2) == 'p' && + *(cursor + 3) == 'l' && + *(cursor + 4) == 'i' && + *(cursor + 5) == 'c' && + *(cursor + 6) == 'i' && + *(cursor + 7) == 't') { + token_stream[(int) index++].kind = Token_explicit; + return; + } + break; + + case 'o': + if (*(cursor + 1) == 'p' && + *(cursor + 2) == 'e' && + *(cursor + 3) == 'r' && + *(cursor + 4) == 'a' && + *(cursor + 5) == 't' && + *(cursor + 6) == 'o' && + *(cursor + 7) == 'r') { + token_stream[(int) index++].kind = Token_operator; + return; + } + break; + + case 'Q': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'O' && + *(cursor + 3) == 'B' && + *(cursor + 4) == 'J' && + *(cursor + 5) == 'E' && + *(cursor + 6) == 'C' && + *(cursor + 7) == 'T') { + token_stream[(int) index++].kind = Token_Q_OBJECT; + return; + } + break; + + case 'r': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 'g' && + *(cursor + 3) == 'i' && + *(cursor + 4) == 's' && + *(cursor + 5) == 't' && + *(cursor + 6) == 'e' && + *(cursor + 7) == 'r') { + token_stream[(int) index++].kind = Token_register; + return; + } + break; + + case 'u': + if (*(cursor + 1) == 'n' && + *(cursor + 2) == 's' && + *(cursor + 3) == 'i' && + *(cursor + 4) == 'g' && + *(cursor + 5) == 'n' && + *(cursor + 6) == 'e' && + *(cursor + 7) == 'd') { + token_stream[(int) index++].kind = Token_unsigned; + return; + } + break; + + case 't': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 'm' && + *(cursor + 3) == 'p' && + *(cursor + 4) == 'l' && + *(cursor + 5) == 'a' && + *(cursor + 6) == 't' && + *(cursor + 7) == 'e') { + token_stream[(int) index++].kind = Token_template; + return; + } + if (*(cursor + 1) == 'y' && + *(cursor + 2) == 'p' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 'n' && + *(cursor + 5) == 'a' && + *(cursor + 6) == 'm' && + *(cursor + 7) == 'e') { + token_stream[(int) index++].kind = Token_typename; + return; + } + break; + + case 'v': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'l' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 't' && + *(cursor + 5) == 'i' && + *(cursor + 6) == 'l' && + *(cursor + 7) == 'e') { + token_stream[(int) index++].kind = Token_volatile; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword9() +{ + switch (*cursor) { + case 'p': + if (*(cursor + 1) == 'r' && + *(cursor + 2) == 'o' && + *(cursor + 3) == 't' && + *(cursor + 4) == 'e' && + *(cursor + 5) == 'c' && + *(cursor + 6) == 't' && + *(cursor + 7) == 'e' && + *(cursor + 8) == 'd') { + token_stream[(int) index++].kind = Token_protected; + return; + } + break; + + case 'n': + if (*(cursor + 1) == 'a' && + *(cursor + 2) == 'm' && + *(cursor + 3) == 'e' && + *(cursor + 4) == 's' && + *(cursor + 5) == 'p' && + *(cursor + 6) == 'a' && + *(cursor + 7) == 'c' && + *(cursor + 8) == 'e') { + token_stream[(int) index++].kind = Token_namespace; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword10() +{ + switch (*cursor) { + case 'c': + if (*(cursor + 1) == 'o' && + *(cursor + 2) == 'n' && + *(cursor + 3) == 's' && + *(cursor + 4) == 't' && + *(cursor + 5) == '_' && + *(cursor + 6) == 'c' && + *(cursor + 7) == 'a' && + *(cursor + 8) == 's' && + *(cursor + 9) == 't') { + token_stream[(int) index++].kind = Token_const_cast; + return; + } + break; + + case 'Q': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'P' && + *(cursor + 3) == 'R' && + *(cursor + 4) == 'O' && + *(cursor + 5) == 'P' && + *(cursor + 6) == 'E' && + *(cursor + 7) == 'R' && + *(cursor + 8) == 'T' && + *(cursor + 9) == 'Y') { + token_stream[(int) index++].kind = Token_Q_PROPERTY; + return; + } + + break; + } + + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword11() +{ + switch (*cursor) { + case 'Q': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'I' && + *(cursor + 3) == 'N' && + *(cursor + 4) == 'V' && + *(cursor + 5) == 'O' && + *(cursor + 6) == 'K' && + *(cursor + 7) == 'A' && + *(cursor + 8) == 'B' && + *(cursor + 9) == 'L' && + *(cursor + 10) == 'E') { + token_stream[(int) index++].kind = Token_Q_INVOKABLE; + return; + } + break; + + case 's': + if (*(cursor + 1) == 't' && + *(cursor + 2) == 'a' && + *(cursor + 3) == 't' && + *(cursor + 4) == 'i' && + *(cursor + 5) == 'c' && + *(cursor + 6) == '_' && + *(cursor + 7) == 'c' && + *(cursor + 8) == 'a' && + *(cursor + 9) == 's' && + *(cursor + 10) == 't') { + token_stream[(int) index++].kind = Token_static_cast; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword12() +{ + switch (*cursor) { + case 'd': + if (*(cursor + 1) == 'y' && + *(cursor + 2) == 'n' && + *(cursor + 3) == 'a' && + *(cursor + 4) == 'm' && + *(cursor + 5) == 'i' && + *(cursor + 6) == 'c' && + *(cursor + 7) == '_' && + *(cursor + 8) == 'c' && + *(cursor + 9) == 'a' && + *(cursor + 10) == 's' && + *(cursor + 11) == 't') { + token_stream[(int) index++].kind = Token_dynamic_cast; + return; + } + break; + + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword13() +{ + switch (*cursor) { + case '_': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'a' && + *(cursor + 3) == 't' && + *(cursor + 4) == 't' && + *(cursor + 5) == 'r' && + *(cursor + 6) == 'i' && + *(cursor + 7) == 'b' && + *(cursor + 8) == 'u' && + *(cursor + 9) == 't' && + *(cursor + 10) == 'e' && + *(cursor + 11) == '_' && + *(cursor + 12) == '_') { + token_stream[(int) index++].kind = Token___attribute__; + return; + } + break; + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword14() +{ + switch (*cursor) { + case 'k': + if (*(cursor + 1) == '_' && + *(cursor + 2) == 'd' && + *(cursor + 3) == 'c' && + *(cursor + 4) == 'o' && + *(cursor + 5) == 'p' && + *(cursor + 6) == '_' && + *(cursor + 7) == 's' && + *(cursor + 8) == 'i' && + *(cursor + 9) == 'g' && + *(cursor + 10) == 'n' && + *(cursor + 11) == 'a' && + *(cursor + 12) == 'l' && + *(cursor + 13) == 's') { + token_stream[(int) index++].kind = Token_k_dcop_signals; + return; + } + break; + } + token_stream[(int) index++].kind = Token_identifier; +} + +void Lexer::scanKeyword16() +{ + switch (*cursor) { + case 'r': + if (*(cursor + 1) == 'e' && + *(cursor + 2) == 'i' && + *(cursor + 3) == 'n' && + *(cursor + 4) == 't' && + *(cursor + 5) == 'e' && + *(cursor + 6) == 'r' && + *(cursor + 7) == 'p' && + *(cursor + 8) == 'r' && + *(cursor + 9) == 'e' && + *(cursor + 10) == 't' && + *(cursor + 11) == '_' && + *(cursor + 12) == 'c' && + *(cursor + 13) == 'a' && + *(cursor + 14) == 's' && + *(cursor + 15) == 't') { + token_stream[(int) index++].kind = Token_reinterpret_cast; + return; + } + break; + } + + token_stream[(int) index++].kind = Token_identifier; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/lexer.h b/parser/lexer.h new file mode 100644 index 000000000..874805325 --- /dev/null +++ b/parser/lexer.h @@ -0,0 +1,290 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef LEXER_H +#define LEXER_H + +#include "symbol.h" + +#include +#include +#include + +struct NameSymbol; +class Lexer; +class Control; + +typedef void (Lexer::*scan_fun_ptr)(); + +class Token +{ +public: + int kind; + std::size_t position; + std::size_t size; + char const *text; + + union { + const NameSymbol *symbol; + std::size_t right_brace; + } extra; +}; + +class LocationTable +{ +private: + LocationTable(const LocationTable &source); + void operator = (const LocationTable &source); + +public: + inline LocationTable(std::size_t size = 1024) + : lines(0), + line_count(0), + current_line(0) { + resize(size); + } + + inline ~LocationTable() { + free(lines); + } + + inline std::size_t size() const { + return line_count; + } + + void resize(std::size_t size) { + Q_ASSERT(size > 0); + lines = (std::size_t*) ::realloc(lines, sizeof(std::size_t) * size); + line_count = size; + } + + void positionAt(std::size_t offset, int *line, int *column) const { + positionAt(offset, (int) current_line, line, column); + } + + void positionAt(std::size_t offset, int max_line, int *line, int *column) const; + + inline std::size_t &operator[](int index) { + return lines[index]; + } + +private: + std::size_t *lines; + std::size_t line_count; + std::size_t current_line; + + friend class Lexer; +}; + +class TokenStream +{ +private: + TokenStream(const TokenStream &); + void operator = (const TokenStream &); + +public: + inline TokenStream(std::size_t size = 1024) + : tokens(0), + index(0), + token_count(0) { + resize(size); + } + + inline ~TokenStream() { + ::free(tokens); + } + + inline std::size_t size() const { + return token_count; + } + + inline std::size_t cursor() const { + return index; + } + + inline void rewind(int i) { + index = i; + } + + void resize(std::size_t size) { + Q_ASSERT(size > 0); + tokens = (Token*) ::realloc(tokens, sizeof(Token) * size); + token_count = size; + } + + inline std::size_t nextToken() { + return index++; + } + + inline int lookAhead(std::size_t i = 0) const { + return tokens[index + i].kind; + } + + inline int kind(std::size_t i) const { + return tokens[i].kind; + } + + inline std::size_t position(std::size_t i) const { + return tokens[i].position; + } + + inline const NameSymbol *symbol(std::size_t i) const { + return tokens[i].extra.symbol; + } + + inline std::size_t matchingBrace(std::size_t i) const { + return tokens[i].extra.right_brace; + } + + inline Token &operator[](int index) { + return tokens[index]; + } + + inline const Token &token(int index) const { + return tokens[index]; + } + +private: + Token *tokens; + std::size_t index; + std::size_t token_count; + +private: + friend class Lexer; +}; + +class LocationManager +{ + LocationManager(LocationManager const &__other); + void operator = (LocationManager const &__other); + +public: + LocationManager(TokenStream &__token_stream, + LocationTable &__location_table, + LocationTable &__line_table): + token_stream(__token_stream), + location_table(__location_table), + line_table(__line_table) {} + + void positionAt(std::size_t offset, int *line, int *column, + QString *filename) const; + + void extract_line(int offset, int *line, QString *filename) const; + + TokenStream &token_stream; + LocationTable &location_table; + LocationTable &line_table; +}; + +class Lexer +{ +public: + Lexer(LocationManager &__location, Control *__control): + _M_location(__location), + token_stream(_M_location.token_stream), + location_table(_M_location.location_table), + line_table(_M_location.line_table), + control(__control) {} + + void tokenize(const char *contents, std::size_t size); + + LocationManager &_M_location; + TokenStream &token_stream; + LocationTable &location_table; + LocationTable &line_table; + +private: + void reportError(const QString& msg); + + void initialize_scan_table(); + void scan_newline(); + void scan_white_spaces(); + void scan_identifier_or_keyword(); + void scan_identifier_or_literal(); + void scan_int_constant(); + void scan_char_constant(); + void scan_string_constant(); + void scan_invalid_input(); + void scan_preprocessor(); + + // keywords + void scanKeyword0(); + void scanKeyword2(); + void scanKeyword3(); + void scanKeyword4(); + void scanKeyword5(); + void scanKeyword6(); + void scanKeyword7(); + void scanKeyword8(); + void scanKeyword9(); + void scanKeyword10(); + void scanKeyword11(); + void scanKeyword12(); + void scanKeyword13(); + void scanKeyword14(); + void scanKeyword16(); + + // operators + void scan_not(); + void scan_remainder(); + void scan_and(); + void scan_left_paren(); + void scan_right_paren(); + void scan_star(); + void scan_plus(); + void scan_comma(); + void scan_minus(); + void scan_dot(); + void scan_divide(); + void scan_colon(); + void scan_semicolon(); + void scan_less(); + void scan_equal(); + void scan_greater(); + void scan_question(); + void scan_left_bracket(); + void scan_right_bracket(); + void scan_xor(); + void scan_left_brace(); + void scan_or(); + void scan_right_brace(); + void scan_tilde(); + void scan_EOF(); + +private: + Control *control; + const unsigned char *cursor; + const unsigned char *begin_buffer; + const unsigned char *end_buffer; + std::size_t index; + + static scan_fun_ptr s_scan_table[]; + static scan_fun_ptr s_scan_keyword_table[]; + static bool s_initialized; +}; + +#endif // LEXER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/list.cpp b/parser/list.cpp new file mode 100644 index 000000000..7482b7392 --- /dev/null +++ b/parser/list.cpp @@ -0,0 +1,28 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "list.h" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/list.h b/parser/list.h new file mode 100644 index 000000000..64aef452f --- /dev/null +++ b/parser/list.h @@ -0,0 +1,100 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef FASTLIST_H +#define FASTLIST_H + +#include "smallobject.h" + +template +struct ListNode { + Tp element; + int index; + mutable const ListNode *next; + + static ListNode *create(const Tp &element, pool *p) { + ListNode *node = new(p->allocate(sizeof(ListNode))) ListNode(); + node->element = element; + node->index = 0; + node->next = node; + + return node; + } + + static ListNode *create(const ListNode *n1, const Tp &element, pool *p) { + ListNode *n2 = ListNode::create(element, p); + + n2->index = n1->index + 1; + n2->next = n1->next; + n1->next = n2; + + return n2; + } + + inline ListNode() { } + + inline const ListNode *at(int index) const { + const ListNode *node = this; + while (index != node->index) + node = node->next; + + return node; + } + + inline bool hasNext() const { + return index < next->index; + } + + inline int count() const { + return 1 + toBack()->index; + } + + inline const ListNode *toFront() const { + return toBack()->next; + } + + inline const ListNode *toBack() const { + const ListNode *node = this; + while (node->hasNext()) + node = node->next; + + return node; + } +}; + +template +inline const ListNode *snoc(const ListNode *list, + const Tp &element, pool *p) +{ + if (!list) + return ListNode::create(element, p); + + return ListNode::create(list->toBack(), element, p); +} + +#endif // FASTLIST_H + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/name_compiler.cpp b/parser/name_compiler.cpp new file mode 100644 index 000000000..4dbd516d6 --- /dev/null +++ b/parser/name_compiler.cpp @@ -0,0 +1,134 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "name_compiler.h" +#include "type_compiler.h" +#include "declarator_compiler.h" +#include "lexer.h" +#include "symbol.h" +#include "binder.h" + +#include + +NameCompiler::NameCompiler(Binder *binder) + : _M_binder(binder), _M_token_stream(binder->tokenStream()) +{ +} + +QString NameCompiler::decode_operator(std::size_t index) const +{ + const Token &tk = _M_token_stream->token((int) index); + return QString::fromUtf8(&tk.text[tk.position], (int) tk.size); +} + +QString NameCompiler::internal_run(AST *node) +{ + _M_name.clear(); + visit(node); + return name(); +} + +void NameCompiler::visitUnqualifiedName(UnqualifiedNameAST *node) +{ + QString tmp_name; + + if (node->tilde) + tmp_name += QLatin1String("~"); + + if (node->id) + tmp_name += _M_token_stream->symbol(node->id)->as_string(); + + if (OperatorFunctionIdAST *op_id = node->operator_id) { +#if defined(__GNUC__) +#warning "NameCompiler::visitUnqualifiedName() -- implement me" +#endif + + if (op_id->op && op_id->op->op) { + tmp_name += QLatin1String("operator"); + tmp_name += decode_operator(op_id->op->op); + if (op_id->op->close) + tmp_name += decode_operator(op_id->op->close); + } else if (op_id->type_specifier) { +#if defined(__GNUC__) +#warning "don't use an hardcoded string as cast' name" +#endif + Token const &tk = _M_token_stream->token((int) op_id->start_token); + Token const &end_tk = _M_token_stream->token((int) op_id->end_token); + tmp_name += QString::fromLatin1(&tk.text[tk.position], + (int)(end_tk.position - tk.position)).trimmed(); + } + } + + _M_name += tmp_name; + if (node->template_arguments) { + // ### cleanup + _M_name.last() += QLatin1String("<"); + visitNodes(this, node->template_arguments); + _M_name.last().truncate(_M_name.last().count() - 1); // remove the last ',' + _M_name.last() += QLatin1String(">"); + } + +} + +void NameCompiler::visitTemplateArgument(TemplateArgumentAST *node) +{ + if (node->type_id && node->type_id->type_specifier) { + TypeCompiler type_cc(_M_binder); + type_cc.run(node->type_id->type_specifier); + + DeclaratorCompiler decl_cc(_M_binder); + decl_cc.run(node->type_id->declarator); + + if (type_cc.isConstant()) + _M_name.last() += "const "; + + QStringList q = type_cc.qualifiedName(); + + if (q.count() == 1) { +#if defined (RXX_RESOLVE_TYPEDEF) // ### it'll break :( + TypeInfo tp; + tp.setQualifiedName(q); + tp = TypeInfo::resolveType(tp, _M_binder->currentScope()->toItem()); + q = tp.qualifiedName(); +#endif + + if (CodeModelItem item = _M_binder->model()->findItem(q, _M_binder->currentScope()->toItem())) { + if (item->name() == q.last()) + q = item->qualifiedName(); + } + } + + _M_name.last() += q.join("::"); + + if (decl_cc.isReference()) + _M_name.last() += "&"; + if (decl_cc.indirection()) + _M_name.last() += QString(decl_cc.indirection(), '*'); + + _M_name.last() += QLatin1String(","); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/name_compiler.h b/parser/name_compiler.h new file mode 100644 index 000000000..66cabaccf --- /dev/null +++ b/parser/name_compiler.h @@ -0,0 +1,69 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef NAME_COMPILER_H +#define NAME_COMPILER_H + +#include "default_visitor.h" +#include + +class TokenStream; +class Binder; + +class NameCompiler: protected DefaultVisitor +{ +public: + NameCompiler(Binder *binder); + + void run(NameAST *node) { + internal_run(node); + } + void run(UnqualifiedNameAST *node) { + internal_run(node); + } + + QString name() const { + return _M_name.join("::"); + } + QStringList qualifiedName() const { + return _M_name; + } + +protected: + virtual void visitUnqualifiedName(UnqualifiedNameAST *node); + virtual void visitTemplateArgument(TemplateArgumentAST *node); + + QString internal_run(AST *node); + QString decode_operator(std::size_t index) const; + +private: + Binder *_M_binder; + TokenStream *_M_token_stream; + QStringList _M_name; +}; + +#endif // NAME_COMPILER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/parser.cpp b/parser/parser.cpp new file mode 100644 index 000000000..d65d5f3e4 --- /dev/null +++ b/parser/parser.cpp @@ -0,0 +1,4056 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + + +// c++ support +#include "parser.h" +#include "tokens.h" +#include "lexer.h" +#include "control.h" + +#include + +#define ADVANCE(tk, descr) \ + { \ + if (token_stream.lookAhead() != tk) { \ + tokenRequiredError(tk); \ + return false; \ + } \ + token_stream.nextToken(); \ + } + +#define ADVANCE_NR(tk, descr) \ + do { \ + if (token_stream.lookAhead() != tk) { \ + tokenRequiredError(tk); \ + } \ + else \ + token_stream.nextToken(); \ + } while (0) + +#define CHECK(tk) \ + do { \ + if (token_stream.lookAhead() != tk) { \ + return false; \ + } \ + token_stream.nextToken(); \ + } while (0) + +#define UPDATE_POS(_node, start, end) \ + do { \ + (_node)->start_token = start; \ + (_node)->end_token = end; \ + } while (0) + +Parser::Parser(Control *c) + : _M_location(token_stream, location_table, line_table), + control(c), + lexer(_M_location, control) +{ + _M_block_errors = false; +} + +Parser::~Parser() +{ +} + +void Parser::advance() +{ + token_stream.nextToken(); +} + +TranslationUnitAST *Parser::parse(const char *contents, + std::size_t size, pool *p) +{ + _M_block_errors = false; + _M_pool = p; + lexer.tokenize(contents, size); + token_stream.nextToken(); // skip the first token + + Lexer *oldLexer = control->changeLexer(&lexer); + Parser *oldParser = control->changeParser(this); + + TranslationUnitAST *ast = 0; + parseTranslationUnit(ast); + + control->changeLexer(oldLexer); + control->changeParser(oldParser); + + return ast; +} + +bool Parser::parseWinDeclSpec(WinDeclSpecAST *&node) +{ + if (token_stream.lookAhead() != Token_identifier) + return false; + + std::size_t start = token_stream.cursor(); + + const NameSymbol *name_symbol = token_stream.symbol(token_stream.cursor()); + QString name = name_symbol->as_string(); + if (name != QLatin1String("__declspec")) + return false; + std::size_t specifier = token_stream.cursor(); + + token_stream.nextToken(); + if (token_stream.lookAhead() != '(') + return false; + + token_stream.nextToken(); + if (token_stream.lookAhead() != Token_identifier) + return false; + std::size_t modifier = token_stream.cursor(); + + token_stream.nextToken(); + if (token_stream.lookAhead() != ')') + return false; + + token_stream.nextToken(); + + node = CreateNode(_M_pool); + node->specifier = specifier; + node->modifier = modifier; + + UPDATE_POS(node, start, token_stream.cursor()); + + return true; +} + +void Parser::tokenRequiredError(int token) +{ + QString err; + + err += "expected token "; + err += "``"; + err += token_name(token); + err += "'' found ``"; + err += token_name(token_stream.lookAhead()); + err += "''"; + + reportError(err); +} + +void Parser::syntaxError() +{ + QString err; + + err += "unexpected token "; + err += "``"; + err += token_name(token_stream.lookAhead()); + err += "''"; + + reportError(err); +} + +void Parser::reportError(const QString& msg) +{ + if (!_M_block_errors) { + int line, column; + QString fileName; + + std::size_t tok = token_stream.cursor(); + location().positionAt(token_stream.position(tok), + &line, &column, &fileName); + + Control::ErrorMessage errmsg; + errmsg.setLine(line + 1); + errmsg.setColumn(column); + errmsg.setFileName(fileName); + errmsg.setMessage(QLatin1String("** PARSER ERROR ") + msg); + control->reportError(errmsg); + } +} + +bool Parser::skipUntil(int token) +{ + while (token_stream.lookAhead()) { + if (token_stream.lookAhead() == token) + return true; + + token_stream.nextToken(); + } + + return false; +} + +bool Parser::skipUntilDeclaration() +{ + while (token_stream.lookAhead()) { + + switch (token_stream.lookAhead()) { + case ';': + case '~': + case Token_scope: + case Token_identifier: + case Token_operator: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_extern: + case Token_namespace: + case Token_using: + case Token_typedef: + case Token_asm: + case Token_template: + case Token_export: + + case Token_const: // cv + case Token_volatile: // cv + + case Token_public: + case Token_protected: + case Token_private: + case Token_signals: // Qt + case Token_slots: // Qt + return true; + + default: + token_stream.nextToken(); + } + } + + return false; +} + +bool Parser::skipUntilStatement() +{ + while (token_stream.lookAhead()) { + switch (token_stream.lookAhead()) { + case ';': + case '{': + case '}': + case Token_const: + case Token_volatile: + case Token_identifier: + case Token_case: + case Token_default: + case Token_if: + case Token_switch: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + case Token_try: + case Token_catch: + case Token_throw: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_class: + case Token_struct: + case Token_union: + case Token_enum: + case Token_scope: + case Token_template: + case Token_using: + return true; + + default: + token_stream.nextToken(); + } + } + + return false; +} + +bool Parser::skip(int l, int r) +{ + int count = 0; + while (token_stream.lookAhead()) { + int tk = token_stream.lookAhead(); + + if (tk == l) + ++count; + else if (tk == r) + --count; + else if (l != '{' && (tk == '{' || tk == '}' || tk == ';')) + return false; + + if (!count) + return true; + + token_stream.nextToken(); + } + + return false; +} + +bool Parser::parseName(NameAST *&node, bool acceptTemplateId) +{ + std::size_t start = token_stream.cursor(); + + WinDeclSpecAST *winDeclSpec = 0; + parseWinDeclSpec(winDeclSpec); + + NameAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_scope) { + ast->global = true; + token_stream.nextToken(); + } + + std::size_t idx = token_stream.cursor(); + + while (true) { + UnqualifiedNameAST *n = 0; + if (!parseUnqualifiedName(n)) + return false; + + if (token_stream.lookAhead() == Token_scope) { + token_stream.nextToken(); + + ast->qualified_names + = snoc(ast->qualified_names, n, _M_pool); + + if (token_stream.lookAhead() == Token_template) { + /// skip optional template #### @todo CHECK + token_stream.nextToken(); + } + } else { + Q_ASSERT(n); + if (!acceptTemplateId) { + token_stream.rewind((int) n->start_token); + parseUnqualifiedName(n, false); + } + + ast->unqualified_name = n; + break; + } + } + + if (idx == token_stream.cursor()) + return false; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTranslationUnit(TranslationUnitAST *&node) +{ + std::size_t start = token_stream.cursor(); + TranslationUnitAST *ast = CreateNode(_M_pool); + + while (token_stream.lookAhead()) { + std::size_t startDecl = token_stream.cursor(); + + DeclarationAST *declaration = 0; + if (parseDeclaration(declaration)) { + ast->declarations = + snoc(ast->declarations, declaration, _M_pool); + } else { + // error recovery + if (startDecl == token_stream.cursor()) { + // skip at least one token + token_stream.nextToken(); + } + + skipUntilDeclaration(); + } + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseDeclaration(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + switch (token_stream.lookAhead()) { + case ';': + token_stream.nextToken(); + return true; + + case Token_extern: + return parseLinkageSpecification(node); + + case Token_namespace: + return parseNamespace(node); + + case Token_using: + return parseUsing(node); + + case Token_typedef: + return parseTypedef(node); + + case Token_asm: + return parseAsmDefinition(node); + + case Token_Q_ENUMS: + return parseQ_ENUMS(node); + + case Token_template: + case Token_export: + return parseTemplateDeclaration(node); + + default: { + const ListNode *cv = 0; + parseCvQualify(cv); + + const ListNode *storageSpec = 0; + parseStorageClassSpecifier(storageSpec); + + parseCvQualify(cv); + + TypeSpecifierAST *spec = 0; + if (parseEnumSpecifier(spec) + || parseClassSpecifier(spec) + || parseForwardDeclarationSpecifier(spec)) { + parseCvQualify(cv); + + spec->cv = cv; + + const ListNode *declarators = 0; + parseInitDeclaratorList(declarators); + ADVANCE(';', ";"); + + SimpleDeclarationAST *ast = + CreateNode(_M_pool); + + ast->storage_specifiers = storageSpec; + ast->type_specifier = spec; + ast->init_declarators = declarators; + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + } // end switch + + token_stream.rewind((int) start); + return parseDeclarationInternal(node); +} + +bool Parser::parseLinkageSpecification(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_extern); + + LinkageSpecificationAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_string_literal) { + ast->extern_type = token_stream.cursor(); + token_stream.nextToken(); + } + + if (token_stream.lookAhead() == '{') + parseLinkageBody(ast->linkage_body); + else if (!parseDeclaration(ast->declaration)) + reportError(("Declaration syntax error")); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseLinkageBody(LinkageBodyAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK('{'); + + LinkageBodyAST *ast = CreateNode(_M_pool); + + while (token_stream.lookAhead()) { + int tk = token_stream.lookAhead(); + + if (tk == '}') + break; + + std::size_t startDecl = token_stream.cursor(); + + DeclarationAST *declaration = 0; + if (parseDeclaration(declaration)) { + ast->declarations = snoc(ast->declarations, declaration, _M_pool); + } else { + // error recovery + if (startDecl == token_stream.cursor()) { + // skip at least one token + token_stream.nextToken(); + } + + skipUntilDeclaration(); + } + } + + if (token_stream.lookAhead() != '}') + reportError(("} expected")); + else + token_stream.nextToken(); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseNamespace(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_namespace); + + std::size_t namespace_name = 0; + if (token_stream.lookAhead() == Token_identifier) { + namespace_name = token_stream.cursor(); + token_stream.nextToken(); + } + + if (token_stream.lookAhead() == '=') { + // namespace alias + token_stream.nextToken(); + + NameAST *name = 0; + if (parseName(name)) { + ADVANCE(';', ";"); + + NamespaceAliasDefinitionAST *ast + = CreateNode(_M_pool); + ast->namespace_name = namespace_name; + ast->alias_name = name; + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + return true; + } else { + reportError(("namespace expected")); + return false; + } + } else if (token_stream.lookAhead() != '{') { + reportError(("{ expected")); + return false; + } + + NamespaceAST *ast = CreateNode(_M_pool); + ast->namespace_name = namespace_name; + parseLinkageBody(ast->linkage_body); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseUsing(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_using); + + if (token_stream.lookAhead() == Token_namespace) + return parseUsingDirective(node); + + UsingAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_typename) { + ast->type_name = token_stream.cursor(); + token_stream.nextToken(); + } + + if (!parseName(ast->name)) + return false; + + ADVANCE(';', ";"); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseUsingDirective(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_namespace); + + NameAST *name = 0; + if (!parseName(name)) { + reportError(("Namespace name expected")); + return false; + } + + ADVANCE(';', ";"); + + UsingDirectiveAST *ast = CreateNode(_M_pool); + ast->name = name; + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + + +bool Parser::parseOperatorFunctionId(OperatorFunctionIdAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_operator); + + OperatorFunctionIdAST *ast = CreateNode(_M_pool); + + if (!parseOperator(ast->op)) { + ast->op = 0; + + // parse cast operator + const ListNode *cv = 0; + parseCvQualify(cv); + + if (!parseSimpleTypeSpecifier(ast->type_specifier)) { + syntaxError(); + return false; + } + + parseCvQualify(cv); + ast->type_specifier->cv = cv; + + PtrOperatorAST *ptr_op = 0; + while (parsePtrOperator(ptr_op)) + ast->ptr_ops = snoc(ast->ptr_ops, ptr_op, _M_pool); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + return true; +} + +bool Parser::parseTemplateArgumentList(const ListNode *&node, + bool reportError) +{ + TemplateArgumentAST *templArg = 0; + if (!parseTemplateArgument(templArg)) + return false; + + node = snoc(node, templArg, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (!parseTemplateArgument(templArg)) { + if (reportError) { + syntaxError(); + break; + } + + node = 0; + return false; + } + + node = snoc(node, templArg, _M_pool); + } + + return true; +} + +bool Parser::parseTypedef(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_typedef); + + TypeSpecifierAST *spec = 0; + if (!parseTypeSpecifierOrClassSpec(spec)) { + reportError(("Need a type specifier to declare")); + return false; + } + + const ListNode *declarators = 0; + if (!parseInitDeclaratorList(declarators)) { + //reportError(("Need an identifier to declare")); + //return false; + } + + ADVANCE(';', ";"); + + TypedefAST *ast = CreateNode(_M_pool); + ast->type_specifier = spec; + ast->init_declarators = declarators; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseAsmDefinition(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ADVANCE(Token_asm, "asm"); + + const ListNode *cv = 0; + parseCvQualify(cv); + +#if defined(__GNUC__) +#warning "implement me" +#endif + skip('(', ')'); + token_stream.nextToken(); + ADVANCE(';', ";"); + + AsmDefinitionAST *ast = CreateNode(_M_pool); + ast->cv = cv; + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTemplateDeclaration(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + std::size_t exported = 0; + if (token_stream.lookAhead() == Token_export) { + exported = token_stream.cursor(); + token_stream.nextToken(); + } + + CHECK(Token_template); + + const ListNode *params = 0; + if (token_stream.lookAhead() == '<') { + token_stream.nextToken(); + parseTemplateParameterList(params); + + ADVANCE('>', ">"); + } + + DeclarationAST *declaration = 0; + if (!parseDeclaration(declaration)) + reportError(("expected a declaration")); + + TemplateDeclarationAST *ast = CreateNode(_M_pool); + ast->exported = exported; + ast->template_parameters = params; + ast->declaration = declaration; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseOperator(OperatorAST *&node) +{ + std::size_t start = token_stream.cursor(); + + OperatorAST *ast = CreateNode(_M_pool); + + switch (token_stream.lookAhead()) { + case Token_new: + case Token_delete: { + ast->op = token_stream.cursor(); + token_stream.nextToken(); + + if (token_stream.lookAhead() == '[' + && token_stream.lookAhead(1) == ']') { + ast->open = token_stream.cursor(); + token_stream.nextToken(); + + ast->close = token_stream.cursor(); + token_stream.nextToken(); + } + } + break; + + case '+': + case '-': + case '*': + case '/': + case '%': + case '^': + case '&': + case '|': + case '~': + case '!': + case '=': + case '<': + case '>': + case ',': + case Token_assign: + case Token_shift: + case Token_eq: + case Token_not_eq: + case Token_leq: + case Token_geq: + case Token_and: + case Token_or: + case Token_incr: + case Token_decr: + case Token_ptrmem: + case Token_arrow: + ast->op = token_stream.cursor(); + token_stream.nextToken(); + break; + + default: + if (token_stream.lookAhead() == '(' + && token_stream.lookAhead(1) == ')') { + ast->op = ast->open = token_stream.cursor(); + token_stream.nextToken(); + ast->close = token_stream.cursor(); + token_stream.nextToken(); + } else if (token_stream.lookAhead() == '[' + && token_stream.lookAhead(1) == ']') { + ast->op = ast->open = token_stream.cursor(); + token_stream.nextToken(); + ast->close = token_stream.cursor(); + token_stream.nextToken(); + } else { + return false; + } + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseCvQualify(const ListNode *&node) +{ + std::size_t start = token_stream.cursor(); + + int tk; + while (0 != (tk = token_stream.lookAhead()) + && (tk == Token_const || tk == Token_volatile)) { + node = snoc(node, token_stream.cursor(), _M_pool); + token_stream.nextToken(); + } + + return start != token_stream.cursor(); +} + +bool Parser::parseSimpleTypeSpecifier(TypeSpecifierAST *&node, + bool onlyIntegral) +{ + std::size_t start = token_stream.cursor(); + bool isIntegral = false; + bool done = false; + + const ListNode *integrals = 0; + + while (!done) { + switch (token_stream.lookAhead()) { + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + integrals = snoc(integrals, token_stream.cursor(), _M_pool); + isIntegral = true; + token_stream.nextToken(); + break; + + default: + done = true; + } + } + + SimpleTypeSpecifierAST *ast = CreateNode(_M_pool); + if (isIntegral) { + ast->integrals = integrals; + } else if (token_stream.lookAhead() == Token___typeof) { + ast->type_of = token_stream.cursor(); + token_stream.nextToken(); + + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + + std::size_t saved = token_stream.cursor(); + parseTypeId(ast->type_id); + if (token_stream.lookAhead() != ')') { + ast->type_id = 0; + token_stream.rewind((int) saved); + parseUnaryExpression(ast->expression); + } + ADVANCE(')', ")"); + } else { + parseUnaryExpression(ast->expression); + } + } else if (onlyIntegral) { + token_stream.rewind((int) start); + return false; + } else { + if (!parseName(ast->name, true)) { + ast->name = 0; + token_stream.rewind((int) start); + return false; + } + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parsePtrOperator(PtrOperatorAST *&node) +{ + int tk = token_stream.lookAhead(); + + if (tk != '&' && tk != '*' + && tk != Token_scope && tk != Token_identifier) { + return false; + } + + std::size_t start = token_stream.cursor(); + + PtrOperatorAST *ast = CreateNode(_M_pool); + + switch (token_stream.lookAhead()) { + case '&': + case '*': + ast->op = token_stream.cursor(); + token_stream.nextToken(); + break; + + case Token_scope: + case Token_identifier: { + if (!parsePtrToMember(ast->mem_ptr)) { + token_stream.rewind((int) start); + return false; + } + } + break; + + default: + Q_ASSERT(0); + break; + } + + parseCvQualify(ast->cv); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTemplateArgument(TemplateArgumentAST *&node) +{ + std::size_t start = token_stream.cursor(); + + TypeIdAST *typeId = 0; + ExpressionAST *expr = 0; + + if (!parseTypeId(typeId) || (token_stream.lookAhead() != ',' + && token_stream.lookAhead() != '>')) { + token_stream.rewind((int) start); + + if (!parseLogicalOrExpression(expr, true)) + return false; + } + + TemplateArgumentAST *ast = CreateNode(_M_pool); + ast->type_id = typeId; + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTypeSpecifier(TypeSpecifierAST *&node) +{ + std::size_t start = token_stream.cursor(); + + const ListNode *cv = 0; + parseCvQualify(cv); + + TypeSpecifierAST *ast = 0; + if (!parseElaboratedTypeSpecifier(ast) && !parseSimpleTypeSpecifier(ast)) { + token_stream.rewind((int) start); + return false; + } + + parseCvQualify(cv); + ast->cv = cv; + + node = ast; + + return true; +} + +bool Parser::parseDeclarator(DeclaratorAST *&node) +{ + std::size_t start = token_stream.cursor(); + + DeclaratorAST *ast = CreateNode(_M_pool); + + //fprintf(stderr, "[%s-%s] ast->ptr_ops: %p\n", __FILE__, __FUNCTION__, ast->ptr_ops); + + DeclaratorAST *decl = 0; + NameAST *declId = 0; + + PtrOperatorAST *ptrOp = 0; + while (parsePtrOperator(ptrOp)) + ast->ptr_ops = snoc(ast->ptr_ops, ptrOp, _M_pool); + + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + + if (!parseDeclarator(decl)) + return false; + + ast->sub_declarator = decl; + + CHECK(')'); + } else { + if (token_stream.lookAhead() == ':') { + // unnamed bitfield + } else if (parseName(declId, true)) { + ast->id = declId; + } else { + token_stream.rewind((int) start); + return false; + } + + if (token_stream.lookAhead() == ':') { + token_stream.nextToken(); + + if (!parseConstantExpression(ast->bit_expression)) + reportError(("Constant expression expected")); + + goto update_pos; + } + } + + { + bool isVector = true; + + while (token_stream.lookAhead() == '[') { + token_stream.nextToken(); + + ExpressionAST *expr = 0; + parseCommaExpression(expr); + + ADVANCE(']', "]"); + + ast->array_dimensions = snoc(ast->array_dimensions, expr, _M_pool); + isVector = true; + } + + bool skipParen = false; + if (token_stream.lookAhead() == Token_identifier + && token_stream.lookAhead(1) == '(' + && token_stream.lookAhead(2) == '(') { + token_stream.nextToken(); + token_stream.nextToken(); + skipParen = true; + } + + int tok = token_stream.lookAhead(); + if (ast->sub_declarator + && !(isVector || tok == '(' || tok == ',' + || tok == ';' || tok == '=')) { + token_stream.rewind((int) start); + return false; + } + + std::size_t index = token_stream.cursor(); + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + + ParameterDeclarationClauseAST *params = 0; + if (!parseParameterDeclarationClause(params)) { + token_stream.rewind((int) index); + goto update_pos; + } + + ast->parameter_declaration_clause = params; + + if (token_stream.lookAhead() != ')') { + token_stream.rewind((int) index); + goto update_pos; + } + + token_stream.nextToken(); // skip ')' + + parseCvQualify(ast->fun_cv); + parseExceptionSpecification(ast->exception_spec); + + if (token_stream.lookAhead() == Token___attribute__) + parse_Attribute__(); + } + + if (skipParen) { + if (token_stream.lookAhead() != ')') + reportError(("')' expected")); + else + token_stream.nextToken(); + } + } + +update_pos: + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseAbstractDeclarator(DeclaratorAST *&node) +{ + std::size_t start = token_stream.cursor(); + + DeclaratorAST *ast = CreateNode(_M_pool); + DeclaratorAST *decl = 0; + + PtrOperatorAST *ptrOp = 0; + while (parsePtrOperator(ptrOp)) + ast->ptr_ops = snoc(ast->ptr_ops, ptrOp, _M_pool); + + int index = (int) token_stream.cursor(); + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + + if (!parseAbstractDeclarator(decl)) { + token_stream.rewind((int) index); + goto label1; + } + + ast->sub_declarator = decl; + + if (token_stream.lookAhead() != ')') { + token_stream.rewind((int) start); + return false; + } + token_stream.nextToken(); + } else if (token_stream.lookAhead() == ':') { + token_stream.nextToken(); + if (!parseConstantExpression(ast->bit_expression)) { + ast->bit_expression = 0; + reportError(("Constant expression expected")); + } + goto update_pos; + } + +label1: { + bool isVector = true; + + while (token_stream.lookAhead() == '[') { + token_stream.nextToken(); + + ExpressionAST *expr = 0; + parseCommaExpression(expr); + + ADVANCE(']', "]"); + + ast->array_dimensions = snoc(ast->array_dimensions, expr, _M_pool); + isVector = true; + } + + int tok = token_stream.lookAhead(); + if (ast->sub_declarator + && !(isVector || tok == '(' || tok == ',' + || tok == ';' || tok == '=')) { + token_stream.rewind((int) start); + return false; + } + + int index = (int) token_stream.cursor(); + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + + ParameterDeclarationClauseAST *params = 0; + if (!parseParameterDeclarationClause(params)) { + token_stream.rewind((int) index); + goto update_pos; + } + + ast->parameter_declaration_clause = params; + + if (token_stream.lookAhead() != ')') { + token_stream.rewind((int) index); + goto update_pos; + } + + token_stream.nextToken(); // skip ')' + + parseCvQualify(ast->fun_cv); + parseExceptionSpecification(ast->exception_spec); + } + } + +update_pos: + if (token_stream.cursor() == start) + return false; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseEnumSpecifier(TypeSpecifierAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_enum); + + NameAST *name = 0; + parseName(name); + + if (token_stream.lookAhead() != '{') { + token_stream.rewind((int) start); + return false; + } + token_stream.nextToken(); + + EnumSpecifierAST *ast = CreateNode(_M_pool); + ast->name = name; + + EnumeratorAST *enumerator = 0; + if (parseEnumerator(enumerator)) { + ast->enumerators = snoc(ast->enumerators, enumerator, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (!parseEnumerator(enumerator)) { + //reportError(("Enumerator expected")); + break; + } + + ast->enumerators = snoc(ast->enumerators, enumerator, _M_pool); + } + } + + ADVANCE_NR('}', "}"); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameterList(const ListNode *&node) +{ + TemplateParameterAST *param = 0; + if (!parseTemplateParameter(param)) + return false; + + node = snoc(node, param, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (!parseTemplateParameter(param)) { + syntaxError(); + break; + } else { + node = snoc(node, param, _M_pool); + } + } + + return true; +} + +bool Parser::parseTemplateParameter(TemplateParameterAST *&node) +{ + std::size_t start = token_stream.cursor(); + TemplateParameterAST *ast = CreateNode(_M_pool); + + int tk = token_stream.lookAhead(); + + if ((tk == Token_class || tk == Token_typename || tk == Token_template) + && parseTypeParameter(ast->type_parameter)) { + // nothing to do + } else if (!parseParameterDeclaration(ast->parameter_declaration)) + return false; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTypeParameter(TypeParameterAST *&node) +{ + std::size_t start = token_stream.cursor(); + + TypeParameterAST *ast = CreateNode(_M_pool); + ast->type = start; + + switch (token_stream.lookAhead()) { + case Token_class: + case Token_typename: { + token_stream.nextToken(); // skip class + + // parse optional name + if (parseName(ast->name, true)) { + if (token_stream.lookAhead() == '=') { + token_stream.nextToken(); + + if (!parseTypeId(ast->type_id)) { + //syntaxError(); + token_stream.rewind((int) start); + return false; + } + } else if (token_stream.lookAhead() != ',' + && token_stream.lookAhead() != '>') { + token_stream.rewind((int) start); + return false; + } + } + } + break; + + case Token_template: { + token_stream.nextToken(); // skip template + ADVANCE('<', "<"); + + if (!parseTemplateParameterList(ast->template_parameters)) + return false; + + ADVANCE('>', ">"); + + if (token_stream.lookAhead() == Token_class) + token_stream.nextToken(); + + // parse optional name + if (parseName(ast->name, true)) { + if (token_stream.lookAhead() == '=') { + token_stream.nextToken(); + + if (!parseTypeId(ast->type_id)) { + syntaxError(); + return false; + } + } + } + + if (token_stream.lookAhead() == '=') { + token_stream.nextToken(); + + parseName(ast->template_name, true); + } + } + break; + + default: + return false; + + } // end switch + + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + return true; +} + +bool Parser::parseStorageClassSpecifier(const ListNode *&node) +{ + std::size_t start = token_stream.cursor(); + + int tk; + while (0 != (tk = token_stream.lookAhead()) + && (tk == Token_friend || tk == Token_auto + || tk == Token_register || tk == Token_static + || tk == Token_extern || tk == Token_mutable)) { + node = snoc(node, token_stream.cursor(), _M_pool); + token_stream.nextToken(); + } + + return start != token_stream.cursor(); +} + +bool Parser::parseFunctionSpecifier(const ListNode *&node) +{ + std::size_t start = token_stream.cursor(); + + int tk; + while (0 != (tk = token_stream.lookAhead()) + && (tk == Token_inline || tk == Token_virtual + || tk == Token_explicit || tk == Token_Q_INVOKABLE)) { + node = snoc(node, token_stream.cursor(), _M_pool); + token_stream.nextToken(); + } + + return start != token_stream.cursor(); +} + +bool Parser::parseTypeId(TypeIdAST *&node) +{ + /// @todo implement the AST for typeId + std::size_t start = token_stream.cursor(); + + TypeSpecifierAST *spec = 0; + if (!parseTypeSpecifier(spec)) { + token_stream.rewind((int) start); + return false; + } + + DeclaratorAST *decl = 0; + parseAbstractDeclarator(decl); + + TypeIdAST *ast = CreateNode(_M_pool); + ast->type_specifier = spec; + ast->declarator = decl; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseInitDeclaratorList(const ListNode *&node) +{ + InitDeclaratorAST *decl = 0; + if (!parseInitDeclarator(decl)) + return false; + + node = snoc(node, decl, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (!parseInitDeclarator(decl)) { + syntaxError(); + break; + } + node = snoc(node, decl, _M_pool); + } + + return true; +} + +bool Parser::parseParameterDeclarationClause(ParameterDeclarationClauseAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ParameterDeclarationClauseAST *ast + = CreateNode(_M_pool); + + if (!parseParameterDeclarationList(ast->parameter_declarations)) { + if (token_stream.lookAhead() == ')') + goto good; + + if (token_stream.lookAhead() == Token_ellipsis + && token_stream.lookAhead(1) == ')') { + ast->ellipsis = token_stream.cursor(); + goto good; + } + + return false; + } + +good: + + if (token_stream.lookAhead() == Token_ellipsis) { + ast->ellipsis = token_stream.cursor(); + token_stream.nextToken(); + } + + /// @todo add ellipsis + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclarationList(const ListNode *&node) +{ + std::size_t start = token_stream.cursor(); + + ParameterDeclarationAST *param = 0; + if (!parseParameterDeclaration(param)) { + token_stream.rewind((int) start); + return false; + } + + node = snoc(node, param, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (token_stream.lookAhead() == Token_ellipsis) + break; + + if (!parseParameterDeclaration(param)) { + token_stream.rewind((int) start); + return false; + } + node = snoc(node, param, _M_pool); + } + + return true; +} + +bool Parser::parseParameterDeclaration(ParameterDeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + const ListNode *storage = 0; + parseStorageClassSpecifier(storage); + + // parse decl spec + TypeSpecifierAST *spec = 0; + if (!parseTypeSpecifier(spec)) { + token_stream.rewind((int) start); + return false; + } + + int index = (int) token_stream.cursor(); + + DeclaratorAST *decl = 0; + if (!parseDeclarator(decl)) { + token_stream.rewind((int) index); + + // try with abstract declarator + parseAbstractDeclarator(decl); + } + + ExpressionAST *expr = 0; + if (token_stream.lookAhead() == '=') { + token_stream.nextToken(); + if (!parseLogicalOrExpression(expr, true)) + reportError(("Expression expected")); + } + + ParameterDeclarationAST *ast = CreateNode(_M_pool); + ast->type_specifier = spec; + ast->declarator = decl; + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parse_Attribute__() +{ + token_stream.nextToken(); + + ADVANCE('(', "("); + + ExpressionAST *expr = 0; + parseExpression(expr); + + if (token_stream.lookAhead() != ')') { + reportError(("')' expected")); + return false; + } else { + token_stream.nextToken(); + } + return true; +} + +QString Parser::tokenText(AST *ast) const +{ + if (!ast) + return QString(); + + int start_token = ast->start_token; + int end_token = ast->end_token; + + Token const &tk = token_stream.token(start_token); + Token const &end_tk = token_stream.token(end_token); + + return QString::fromLatin1(&tk.text[tk.position], + (int)(end_tk.position - tk.position)).trimmed(); +} + +bool Parser::parseForwardDeclarationSpecifier(TypeSpecifierAST *&node) +{ + std::size_t start = token_stream.cursor(); + + int kind = token_stream.lookAhead(); + if (kind != Token_class && kind != Token_struct && kind != Token_union) + return false; + + std::size_t class_key = token_stream.cursor(); + token_stream.nextToken(); + + NameAST *name = 0; + if (!parseName(name, false)) { + token_stream.rewind((int) start); + return false; + } + + BaseClauseAST *bases = 0; + if (token_stream.lookAhead() == ':') { + if (!parseBaseClause(bases)) { + token_stream.rewind((int) start); + return false; + } + } + + if (token_stream.lookAhead() != ';') { + token_stream.rewind((int) start); + return false; + } + + ForwardDeclarationSpecifierAST *ast = CreateNode(_M_pool); + ast->class_key = class_key; + ast->name = name; + ast->base_clause = bases; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseClassSpecifier(TypeSpecifierAST *&node) +{ + std::size_t start = token_stream.cursor(); + + int kind = token_stream.lookAhead(); + if (kind != Token_class && kind != Token_struct && kind != Token_union) + return false; + + std::size_t class_key = token_stream.cursor(); + token_stream.nextToken(); + + WinDeclSpecAST *winDeclSpec = 0; + parseWinDeclSpec(winDeclSpec); + + if (token_stream.lookAhead() == Token___attribute__) + parse_Attribute__(); + + while (token_stream.lookAhead() == Token_identifier + && token_stream.lookAhead(1) == Token_identifier) + token_stream.nextToken(); + + NameAST *name = 0; + parseName(name, true); + + BaseClauseAST *bases = 0; + + if (token_stream.lookAhead() == ':') { + if (!parseBaseClause(bases)) + skipUntil('{'); + } + + if (token_stream.lookAhead() != '{') { + + token_stream.rewind((int) start); + return false; + } + + ADVANCE('{', "{"); + + ClassSpecifierAST *ast = CreateNode(_M_pool); + ast->win_decl_specifiers = winDeclSpec; + ast->class_key = class_key; + ast->name = name; + ast->base_clause = bases; + + while (token_stream.lookAhead()) { + if (token_stream.lookAhead() == '}') + break; + + std::size_t startDecl = token_stream.cursor(); + + DeclarationAST *memSpec = 0; + if (!parseMemberSpecification(memSpec)) { + if (startDecl == token_stream.cursor()) + token_stream.nextToken(); // skip at least one token + skipUntilDeclaration(); + } else + ast->member_specs = snoc(ast->member_specs, memSpec, _M_pool); + } + + ADVANCE_NR('}', "}"); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseAccessSpecifier(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + const ListNode *specs = 0; + + bool done = false; + while (!done) { + switch (token_stream.lookAhead()) { + case Token_signals: + case Token_slots: + case Token_k_dcop: + case Token_k_dcop_signals: + case Token_public: + case Token_protected: + case Token_private: + specs = snoc(specs, token_stream.cursor(), _M_pool); + token_stream.nextToken(); + break; + + default: + done = true; + break; + } + } + + if (!specs) + return false; + + ADVANCE(':', ":"); + + AccessSpecifierAST *ast = CreateNode(_M_pool); + ast->specs = specs; + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseMemberSpecification(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (token_stream.lookAhead() == ';') { + token_stream.nextToken(); + return true; + } else if (token_stream.lookAhead() == Token_Q_OBJECT + || token_stream.lookAhead() == Token_K_DCOP) { + token_stream.nextToken(); + return true; + } else if (parseTypedef(node)) { + return true; + } else if (parseUsing(node)) { + return true; + } else if (parseTemplateDeclaration(node)) { + return true; + } else if (parseAccessSpecifier(node)) { + return true; + } else if (parseQ_PROPERTY(node)) { + return true; + } else if (parseQ_ENUMS(node)) { + return true; + } + + token_stream.rewind((int) start); + + const ListNode *cv = 0; + parseCvQualify(cv); + + const ListNode *storageSpec = 0; + parseStorageClassSpecifier(storageSpec); + + parseCvQualify(cv); + + TypeSpecifierAST *spec = 0; + if (parseEnumSpecifier(spec) || parseClassSpecifier(spec)) { + parseCvQualify(cv); + spec->cv = cv; + + const ListNode *declarators = 0; + parseInitDeclaratorList(declarators); + ADVANCE(';', ";"); + + SimpleDeclarationAST *ast = CreateNode(_M_pool); + ast->type_specifier = spec; + ast->init_declarators = declarators; + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + + token_stream.rewind((int) start); + return parseDeclarationInternal(node); +} + +bool Parser::parseCtorInitializer(CtorInitializerAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(':'); + + CtorInitializerAST *ast = CreateNode(_M_pool); + ast->colon = start; + + if (!parseMemInitializerList(ast->member_initializers)) + reportError(("Member initializers expected")); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseElaboratedTypeSpecifier(TypeSpecifierAST *&node) +{ + std::size_t start = token_stream.cursor(); + + int tk = token_stream.lookAhead(); + if (tk == Token_class + || tk == Token_struct + || tk == Token_union + || tk == Token_enum + || tk == Token_typename) { + std::size_t type = token_stream.cursor(); + token_stream.nextToken(); + + NameAST *name = 0; + if (parseName(name, true)) { + ElaboratedTypeSpecifierAST *ast + = CreateNode(_M_pool); + + ast->type = type; + ast->name = name; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + + token_stream.rewind((int) start); + return false; +} + +bool Parser::parseExceptionSpecification(ExceptionSpecificationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_throw); + ADVANCE('(', "("); + + ExceptionSpecificationAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_ellipsis) { + ast->ellipsis = token_stream.cursor(); + token_stream.nextToken(); + } else { + parseTypeIdList(ast->type_ids); + } + + ADVANCE(')', ")"); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseEnumerator(EnumeratorAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_identifier); + std::size_t id = token_stream.cursor() - 1; + + EnumeratorAST *ast = CreateNode(_M_pool); + ast->id = id; + + if (token_stream.lookAhead() == '=') { + token_stream.nextToken(); + + if (!parseConstantExpression(ast->expression)) + reportError(("Constant expression expected")); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseInitDeclarator(InitDeclaratorAST *&node) +{ + std::size_t start = token_stream.cursor(); + + DeclaratorAST *decl = 0; + if (!parseDeclarator(decl)) + return false; + + if (token_stream.lookAhead(0) == Token_asm) { + token_stream.nextToken(); + skip('(', ')'); + token_stream.nextToken(); + } + + InitializerAST *init = 0; + parseInitializer(init); + + InitDeclaratorAST *ast = CreateNode(_M_pool); + ast->declarator = decl; + ast->initializer = init; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseBaseClause(BaseClauseAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(':'); + + BaseSpecifierAST *baseSpec = 0; + if (!parseBaseSpecifier(baseSpec)) + return false; + + BaseClauseAST *ast = CreateNode(_M_pool); + ast->base_specifiers = snoc(ast->base_specifiers, baseSpec, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (!parseBaseSpecifier(baseSpec)) { + reportError(("Base class specifier expected")); + break; + } + ast->base_specifiers = snoc(ast->base_specifiers, baseSpec, _M_pool); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseInitializer(InitializerAST *&node) +{ + std::size_t start = token_stream.cursor(); + + int tk = token_stream.lookAhead(); + if (tk != '=' && tk != '(') + return false; + + InitializerAST *ast = CreateNode(_M_pool); + + if (tk == '=') { + token_stream.nextToken(); + + if (!parseInitializerClause(ast->initializer_clause)) + reportError(("Initializer clause expected")); + + } else if (tk == '(') { + token_stream.nextToken(); + parseCommaExpression(ast->expression); + CHECK(')'); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseMemInitializerList(const ListNode *&node) +{ + MemInitializerAST *init = 0; + + if (!parseMemInitializer(init)) + return false; + + node = snoc(node, init, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + + if (!parseMemInitializer(init)) + break; + + node = snoc(node, init, _M_pool); + } + + return true; +} + +bool Parser::parseMemInitializer(MemInitializerAST *&node) +{ + std::size_t start = token_stream.cursor(); + + NameAST *initId = 0; + if (!parseName(initId, true)) { + reportError(("Identifier expected")); + return false; + } + + ADVANCE('(', "("); + ExpressionAST *expr = 0; + parseCommaExpression(expr); + ADVANCE(')', ")"); + + MemInitializerAST *ast = CreateNode(_M_pool); + ast->initializer_id = initId; + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseTypeIdList(const ListNode *&node) +{ + TypeIdAST *typeId = 0; + if (!parseTypeId(typeId)) + return false; + + node = snoc(node, typeId, _M_pool); + + while (token_stream.lookAhead() == ',') { + token_stream.nextToken(); + if (parseTypeId(typeId)) { + node = snoc(node, typeId, _M_pool); + } else { + reportError(("Type id expected")); + break; + } + } + + return true; +} + +bool Parser::parseBaseSpecifier(BaseSpecifierAST *&node) +{ + std::size_t start = token_stream.cursor(); + + BaseSpecifierAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_virtual) { + ast->virt = token_stream.cursor(); + token_stream.nextToken(); + + int tk = token_stream.lookAhead(); + if (tk == Token_public || tk == Token_protected + || tk == Token_private) { + ast->access_specifier = token_stream.cursor(); + token_stream.nextToken(); + } + } else { + int tk = token_stream.lookAhead(); + if (tk == Token_public || tk == Token_protected + || tk == Token_private) { + ast->access_specifier = token_stream.cursor(); + token_stream.nextToken(); + } + + if (token_stream.lookAhead() == Token_virtual) { + ast->virt = token_stream.cursor(); + token_stream.nextToken(); + } + } + + if (!parseName(ast->name, true)) + reportError(("Class name expected")); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseInitializerClause(InitializerClauseAST *&node) +{ + std::size_t start = token_stream.cursor(); + + InitializerClauseAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == '{') { +#if defined(__GNUC__) +#warning "implement me" +#endif + if (skip('{', '}')) + token_stream.nextToken(); + else + reportError(("} missing")); + } else { + if (!parseAssignmentExpression(ast->expression)) + reportError(("Expression expected")); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parsePtrToMember(PtrToMemberAST *&node) +{ +#if defined(__GNUC__) +#warning "implemente me (AST)" +#endif + + std::size_t start = token_stream.cursor(); + + std::size_t global_scope = 0; + if (token_stream.lookAhead() == Token_scope) { + global_scope = token_stream.cursor(); + token_stream.nextToken(); + } + + UnqualifiedNameAST *name = 0; + while (token_stream.lookAhead() == Token_identifier) { + if (!parseUnqualifiedName(name)) + break; + + if (token_stream.lookAhead() == Token_scope + && token_stream.lookAhead(1) == '*') { + token_stream.nextToken(); + token_stream.nextToken(); + + PtrToMemberAST *ast = CreateNode(_M_pool); + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + + if (token_stream.lookAhead() == Token_scope) + token_stream.nextToken(); + } + + token_stream.rewind((int) start); + return false; +} + +bool Parser::parseUnqualifiedName(UnqualifiedNameAST *&node, + bool parseTemplateId) +{ + std::size_t start = token_stream.cursor(); + + std::size_t tilde = 0; + std::size_t id = 0; + OperatorFunctionIdAST *operator_id = 0; + + if (token_stream.lookAhead() == Token_identifier) { + id = token_stream.cursor(); + token_stream.nextToken(); + } else if (token_stream.lookAhead() == '~' + && token_stream.lookAhead(1) == Token_identifier) { + tilde = token_stream.cursor(); + token_stream.nextToken(); // skip ~ + + id = token_stream.cursor(); + token_stream.nextToken(); // skip classname + } else if (token_stream.lookAhead() == Token_operator) { + if (!parseOperatorFunctionId(operator_id)) + return false; + } else { + return false; + } + + UnqualifiedNameAST *ast = CreateNode(_M_pool); + ast->tilde = tilde; + ast->id = id; + ast->operator_id = operator_id; + + if (parseTemplateId && !tilde) { + std::size_t index = token_stream.cursor(); + + if (token_stream.lookAhead() == '<') { + token_stream.nextToken(); + + // optional template arguments + parseTemplateArgumentList(ast->template_arguments); + + if (token_stream.lookAhead() == '>') { + token_stream.nextToken(); + } else { + ast->template_arguments = 0; + token_stream.rewind((int) index); + } + } + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseStringLiteral(StringLiteralAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (token_stream.lookAhead() != Token_string_literal) + return false; + + StringLiteralAST *ast = CreateNode(_M_pool); + + while (token_stream.lookAhead() == Token_string_literal) { + ast->literals = snoc(ast->literals, token_stream.cursor(), _M_pool); + token_stream.nextToken(); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseExpressionStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ExpressionAST *expr = 0; + parseCommaExpression(expr); + + ADVANCE(';', ";"); + + ExpressionStatementAST *ast = CreateNode(_M_pool); + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + switch (token_stream.lookAhead()) { + case Token_while: + return parseWhileStatement(node); + + case Token_do: + return parseDoStatement(node); + + case Token_for: + return parseForStatement(node); + + case Token_if: + return parseIfStatement(node); + + case Token_switch: + return parseSwitchStatement(node); + + case Token_try: + return parseTryBlockStatement(node); + + case Token_case: + case Token_default: + return parseLabeledStatement(node); + + case Token_break: + case Token_continue: +#if defined(__GNUC__) +#warning "implement me" +#endif + token_stream.nextToken(); + ADVANCE(';', ";"); + return true; + + case Token_goto: +#if defined(__GNUC__) +#warning "implement me" +#endif + token_stream.nextToken(); + ADVANCE(Token_identifier, "identifier"); + ADVANCE(';', ";"); + return true; + + case Token_return: { + token_stream.nextToken(); + ExpressionAST *expr = 0; + parseCommaExpression(expr); + + ADVANCE(';', ";"); + + ReturnStatementAST *ast = CreateNode(_M_pool); + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case '{': + return parseCompoundStatement(node); + + case Token_identifier: + if (parseLabeledStatement(node)) + return true; + break; + } + + return parseExpressionOrDeclarationStatement(node); +} + +bool Parser::parseExpressionOrDeclarationStatement(StatementAST *&node) +{ + bool blocked = block_errors(true); + + std::size_t start = token_stream.cursor(); + + StatementAST *decl_ast = 0; + bool maybe_amb = parseDeclarationStatement(decl_ast); + maybe_amb &= token_stream.kind(token_stream.cursor() - 1) == ';'; + + std::size_t end = token_stream.cursor(); + + token_stream.rewind((int) start); + StatementAST *expr_ast = 0; + maybe_amb &= parseExpressionStatement(expr_ast); + maybe_amb &= token_stream.kind(token_stream.cursor() - 1) == ';'; + + if (maybe_amb) { + Q_ASSERT(decl_ast && expr_ast); + ExpressionOrDeclarationStatementAST *ast = + CreateNode(_M_pool); + ast->declaration = decl_ast; + ast->expression = expr_ast; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } else { + token_stream.rewind((int) std::max(end, token_stream.cursor())); + + node = decl_ast; + if (!node) + node = expr_ast; + } + + block_errors(blocked); + + if (!node) + syntaxError(); + + return node != 0; +} + +bool Parser::parseCondition(ConditionAST *&node, bool initRequired) +{ + std::size_t start = token_stream.cursor(); + + ConditionAST *ast = CreateNode(_M_pool); + TypeSpecifierAST *spec = 0; + + if (parseTypeSpecifier(spec)) { + ast->type_specifier = spec; + + std::size_t declarator_start = token_stream.cursor(); + + DeclaratorAST *decl = 0; + if (!parseDeclarator(decl)) { + token_stream.rewind((int) declarator_start); + if (!initRequired && !parseAbstractDeclarator(decl)) + decl = 0; + } + + if (decl && (!initRequired || token_stream.lookAhead() == '=')) { + ast->declarator = decl; + + if (token_stream.lookAhead() == '=') { + token_stream.nextToken(); + + parseExpression(ast->expression); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + + token_stream.rewind((int) start); + + if (!parseCommaExpression(ast->expression)) + return false; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + + +bool Parser::parseWhileStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ADVANCE(Token_while, "while"); + ADVANCE('(' , "("); + + ConditionAST *cond = 0; + if (!parseCondition(cond)) { + reportError(("condition expected")); + return false; + } + ADVANCE(')', ")"); + + StatementAST *body = 0; + if (!parseStatement(body)) { + reportError(("statement expected")); + return false; + } + + WhileStatementAST *ast = CreateNode(_M_pool); + ast->condition = cond; + ast->statement = body; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseDoStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ADVANCE(Token_do, "do"); + + StatementAST *body = 0; + if (!parseStatement(body)) { + reportError(("statement expected")); + //return false; + } + + ADVANCE_NR(Token_while, "while"); + ADVANCE_NR('(' , "("); + + ExpressionAST *expr = 0; + if (!parseCommaExpression(expr)) { + reportError(("expression expected")); + //return false; + } + + ADVANCE_NR(')', ")"); + ADVANCE_NR(';', ";"); + + DoStatementAST *ast = CreateNode(_M_pool); + ast->statement = body; + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseForStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ADVANCE(Token_for, "for"); + ADVANCE('(', "("); + + StatementAST *init = 0; + if (!parseForInitStatement(init)) { + reportError(("for initialization expected")); + return false; + } + + ConditionAST *cond = 0; + parseCondition(cond); + ADVANCE(';', ";"); + + ExpressionAST *expr = 0; + parseCommaExpression(expr); + ADVANCE(')', ")"); + + StatementAST *body = 0; + if (!parseStatement(body)) + return false; + + ForStatementAST *ast = CreateNode(_M_pool); + ast->init_statement = init; + ast->condition = cond; + ast->expression = expr; + ast->statement = body; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseForInitStatement(StatementAST *&node) +{ + if (parseDeclarationStatement(node)) + return true; + + return parseExpressionStatement(node); +} + +bool Parser::parseCompoundStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK('{'); + + CompoundStatementAST *ast = CreateNode(_M_pool); + + while (token_stream.lookAhead()) { + if (token_stream.lookAhead() == '}') + break; + + std::size_t startStmt = token_stream.cursor(); + + StatementAST *stmt = 0; + if (!parseStatement(stmt)) { + if (startStmt == token_stream.cursor()) + token_stream.nextToken(); + + skipUntilStatement(); + } else { + ast->statements = snoc(ast->statements, stmt, _M_pool); + } + } + + ADVANCE_NR('}', "}"); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseIfStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + ADVANCE(Token_if, "if"); + + ADVANCE('(' , "("); + + IfStatementAST *ast = CreateNode(_M_pool); + + ConditionAST *cond = 0; + if (!parseCondition(cond)) { + reportError(("condition expected")); + return false; + } + ADVANCE(')', ")"); + + StatementAST *stmt = 0; + if (!parseStatement(stmt)) { + reportError(("statement expected")); + return false; + } + + ast->condition = cond; + ast->statement = stmt; + + if (token_stream.lookAhead() == Token_else) { + token_stream.nextToken(); + + if (!parseStatement(ast->else_statement)) { + reportError(("statement expected")); + return false; + } + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseSwitchStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + ADVANCE(Token_switch, "switch"); + + ADVANCE('(' , "("); + + ConditionAST *cond = 0; + if (!parseCondition(cond)) { + reportError(("condition expected")); + return false; + } + ADVANCE(')', ")"); + + StatementAST *stmt = 0; + if (!parseCompoundStatement(stmt)) { + syntaxError(); + return false; + } + + SwitchStatementAST *ast = CreateNode(_M_pool); + ast->condition = cond; + ast->statement = stmt; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseLabeledStatement(StatementAST *&node) +{ + switch (token_stream.lookAhead()) { + case Token_identifier: + case Token_default: + if (token_stream.lookAhead(1) == ':') { + token_stream.nextToken(); + token_stream.nextToken(); + + StatementAST *stmt = 0; + if (parseStatement(stmt)) { + node = stmt; + return true; + } + } + break; + + case Token_case: { + token_stream.nextToken(); + ExpressionAST *expr = 0; + if (!parseConstantExpression(expr)) { + reportError(("expression expected")); + } else if (token_stream.lookAhead() == Token_ellipsis) { + token_stream.nextToken(); + + ExpressionAST *expr2 = 0; + if (!parseConstantExpression(expr2)) + reportError(("expression expected")); + } + ADVANCE(':', ":"); + + StatementAST *stmt = 0; + if (parseStatement(stmt)) { + node = stmt; + return true; + } + } + break; + + } + + return false; +} + +bool Parser::parseBlockDeclaration(DeclarationAST *&node) +{ + switch (token_stream.lookAhead()) { + case Token_typedef: + return parseTypedef(node); + case Token_using: + return parseUsing(node); + case Token_asm: + return parseAsmDefinition(node); + case Token_namespace: + return parseNamespaceAliasDefinition(node); + } + + std::size_t start = token_stream.cursor(); + + const ListNode *cv = 0; + parseCvQualify(cv); + + const ListNode *storageSpec = 0; + parseStorageClassSpecifier(storageSpec); + + parseCvQualify(cv); + + TypeSpecifierAST *spec = 0; + if (!parseTypeSpecifierOrClassSpec(spec)) { // replace with simpleTypeSpecifier?!?! + token_stream.rewind((int) start); + return false; + } + + parseCvQualify(cv); + spec->cv = cv; + + const ListNode *declarators = 0; + parseInitDeclaratorList(declarators); + + if (token_stream.lookAhead() != ';') { + token_stream.rewind((int) start); + return false; + } + token_stream.nextToken(); + + SimpleDeclarationAST *ast = CreateNode(_M_pool); + ast->type_specifier = spec; + ast->init_declarators = declarators; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseNamespaceAliasDefinition(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_namespace); + + NamespaceAliasDefinitionAST *ast + = CreateNode(_M_pool); + + ADVANCE(Token_identifier, "identifier"); + ast->namespace_name = token_stream.cursor() - 1; + + ADVANCE('=', "="); + + if (!parseName(ast->alias_name)) + reportError(("Namespace name expected")); + + ADVANCE(';', ";"); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseDeclarationStatement(StatementAST *&node) +{ + std::size_t start = token_stream.cursor(); + + DeclarationAST *decl = 0; + if (!parseBlockDeclaration(decl)) + return false; + + DeclarationStatementAST *ast = CreateNode(_M_pool); + ast->declaration = decl; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseDeclarationInternal(DeclarationAST *&node) +{ + std::size_t start = token_stream.cursor(); + + // that is for the case '__declspec(dllexport) int ...' or + // '__declspec(dllexport) inline int ...', etc. + WinDeclSpecAST *winDeclSpec = 0; + parseWinDeclSpec(winDeclSpec); + + const ListNode *funSpec = 0; + bool hasFunSpec = parseFunctionSpecifier(funSpec); + + const ListNode *cv = 0; + parseCvQualify(cv); + + const ListNode *storageSpec = 0; + bool hasStorageSpec = parseStorageClassSpecifier(storageSpec); + + if (hasStorageSpec && !hasFunSpec) + hasFunSpec = parseFunctionSpecifier(funSpec); + + // that is for the case 'friend __declspec(dllexport) ....' + parseWinDeclSpec(winDeclSpec); + + if (!cv) + parseCvQualify(cv); + + int index = (int) token_stream.cursor(); + NameAST *name = 0; + if (parseName(name, true) && token_stream.lookAhead() == '(') { + // no type specifier, maybe a constructor or a cast operator?? + + token_stream.rewind((int) index); + + InitDeclaratorAST *declarator = 0; + if (parseInitDeclarator(declarator)) { + switch (token_stream.lookAhead()) { + case ';': { + token_stream.nextToken(); + + SimpleDeclarationAST *ast + = CreateNode(_M_pool); + + ast->storage_specifiers = storageSpec; + ast->function_specifiers = funSpec; + ast->init_declarators = snoc(ast->init_declarators, + declarator, _M_pool); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case ':': { + CtorInitializerAST *ctorInit = 0; + StatementAST *funBody = 0; + + if (parseCtorInitializer(ctorInit) + && parseFunctionBody(funBody)) { + FunctionDefinitionAST *ast + = CreateNode(_M_pool); + + ast->storage_specifiers = storageSpec; + ast->function_specifiers = funSpec; + ast->init_declarator = declarator; + ast->function_body = funBody; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + break; + + case '{': { + StatementAST *funBody = 0; + if (parseFunctionBody(funBody)) { + FunctionDefinitionAST *ast + = CreateNode(_M_pool); + + ast->storage_specifiers = storageSpec; + ast->function_specifiers = funSpec; + ast->init_declarator = declarator; + ast->function_body = funBody; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + break; + + case '(': + case '[': + // ops!! it seems a declarator + goto start_decl; + break; + } + + } + } + +start_decl: + token_stream.rewind((int) index); + + if (token_stream.lookAhead() == Token_const + && token_stream.lookAhead(1) == Token_identifier + && token_stream.lookAhead(2) == '=') { + // constant definition + token_stream.nextToken(); // skip const + + const ListNode *declarators = 0; + if (!parseInitDeclaratorList(declarators)) { + syntaxError(); + return false; + } + + ADVANCE(';', ";"); + +#if defined(__GNUC__) +#warning "mark the ast as constant" +#endif + SimpleDeclarationAST *ast = CreateNode(_M_pool); + ast->init_declarators = declarators; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + + TypeSpecifierAST *spec = 0; + if (parseTypeSpecifier(spec)) { + Q_ASSERT(spec); + + if (!hasFunSpec) + parseFunctionSpecifier(funSpec); // e.g. "void inline" + + spec->cv = cv; + + const ListNode *declarators = 0; + InitDeclaratorAST *decl = 0; + int startDeclarator = (int) token_stream.cursor(); + bool maybeFunctionDefinition = false; + + if (token_stream.lookAhead() != ';') { + if (parseInitDeclarator(decl) && token_stream.lookAhead() == '{') { + // function definition + maybeFunctionDefinition = true; + } else { + token_stream.rewind((int) startDeclarator); + if (!parseInitDeclaratorList(declarators)) { + syntaxError(); + return false; + } + } + } + + switch (token_stream.lookAhead()) { + case ';': { + token_stream.nextToken(); + SimpleDeclarationAST *ast + = CreateNode(_M_pool); + + ast->storage_specifiers = storageSpec; + ast->function_specifiers = funSpec; + ast->type_specifier = spec; + ast->win_decl_specifiers = winDeclSpec; + ast->init_declarators = declarators; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case '{': { + if (!maybeFunctionDefinition) { + syntaxError(); + return false; + } + + StatementAST *funBody = 0; + if (parseFunctionBody(funBody)) { + FunctionDefinitionAST *ast + = CreateNode(_M_pool); + + ast->win_decl_specifiers = winDeclSpec; + ast->storage_specifiers = storageSpec; + ast->function_specifiers = funSpec; + ast->type_specifier = spec; + ast->init_declarator = decl; + ast->function_body = funBody; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + break; + } // end switch + } + + syntaxError(); + return false; +} + +bool Parser::skipFunctionBody(StatementAST *&) +{ +#if defined(__GNUC__) +#warning "Parser::skipFunctionBody() -- implement me" +#endif + Q_ASSERT(0); // ### not implemented + return 0; +} + +bool Parser::parseFunctionBody(StatementAST *&node) +{ + if (control->skipFunctionBody()) + return skipFunctionBody(node); + + return parseCompoundStatement(node); +} + +bool Parser::parseTypeSpecifierOrClassSpec(TypeSpecifierAST *&node) +{ + if (parseClassSpecifier(node)) + return true; + else if (parseEnumSpecifier(node)) + return true; + else if (parseTypeSpecifier(node)) + return true; + + return false; +} + +bool Parser::parseTryBlockStatement(StatementAST *&node) +{ +#if defined(__GNUC__) +#warning "implement me" +#endif + CHECK(Token_try); + + StatementAST *stmt = 0; + if (!parseCompoundStatement(stmt)) { + syntaxError(); + return false; + } + + if (token_stream.lookAhead() != Token_catch) { + reportError(("catch expected")); + return false; + } + + while (token_stream.lookAhead() == Token_catch) { + token_stream.nextToken(); + ADVANCE('(', "("); + ConditionAST *cond = 0; + if (token_stream.lookAhead() == Token_ellipsis) { + token_stream.nextToken(); + } else if (!parseCondition(cond, false)) { + reportError(("condition expected")); + return false; + } + ADVANCE(')', ")"); + + StatementAST *body = 0; + if (!parseCompoundStatement(body)) { + syntaxError(); + return false; + } + } + + node = stmt; + return true; +} + +bool Parser::parsePrimaryExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + PrimaryExpressionAST *ast = CreateNode(_M_pool); + + switch (token_stream.lookAhead()) { + case Token_string_literal: + parseStringLiteral(ast->literal); + break; + + case Token_number_literal: + case Token_char_literal: + case Token_true: + case Token_false: + case Token_this: + ast->token = token_stream.cursor(); + token_stream.nextToken(); + break; + + case '(': + token_stream.nextToken(); + + if (token_stream.lookAhead() == '{') { + if (!parseCompoundStatement(ast->expression_statement)) + return false; + } else { + if (!parseExpression(ast->sub_expression)) + return false; + } + + CHECK(')'); + break; + + default: + if (!parseName(ast->name)) + return false; + + break; + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + + +/* + postfix-expression-internal: + [ expression ] + ( expression-list [opt] ) + (.|->) template [opt] id-expression + (.|->) pseudo-destructor-name + ++ + -- +*/ +bool Parser::parsePostfixExpressionInternal(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + switch (token_stream.lookAhead()) { + case '[': { + token_stream.nextToken(); + ExpressionAST *expr = 0; + parseExpression(expr); + CHECK(']'); + + SubscriptExpressionAST *ast + = CreateNode(_M_pool); + + ast->subscript = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case '(': { + token_stream.nextToken(); + ExpressionAST *expr = 0; + parseExpression(expr); + CHECK(')'); + + FunctionCallAST *ast = CreateNode(_M_pool); + ast->arguments = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case '.': + case Token_arrow: { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + std::size_t templ = 0; + if (token_stream.lookAhead() == Token_template) { + templ = token_stream.cursor(); + token_stream.nextToken(); + } + + int saved = int(token_stream.cursor()); + NameAST *name = 0; + + if (parseName(name, true) && name->unqualified_name + && name->unqualified_name->template_arguments + && token_stream.lookAhead() == '(') { + // a template method call + // ### reverse the logic + } else { + token_stream.rewind(saved); + name = 0; + + if (!parseName(name, templ != 0)) + return false; + } + + ClassMemberAccessAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->name = name; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case Token_incr: + case Token_decr: { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + IncrDecrExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + default: + return false; + } +} + +/* + postfix-expression: + simple-type-specifier ( expression-list [opt] ) + primary-expression postfix-expression-internal* +*/ +bool Parser::parsePostfixExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + switch (token_stream.lookAhead()) { + case Token_dynamic_cast: + case Token_static_cast: + case Token_reinterpret_cast: + case Token_const_cast: { + std::size_t castOp = token_stream.cursor(); + token_stream.nextToken(); + + CHECK('<'); + TypeIdAST *typeId = 0; + parseTypeId(typeId); + CHECK('>'); + + CHECK('('); + ExpressionAST *expr = 0; + parseCommaExpression(expr); + CHECK(')'); + + CppCastExpressionAST *ast = CreateNode(_M_pool); + ast->op = castOp; + ast->type_id = typeId; + ast->expression = expr; + + ExpressionAST *e = 0; + while (parsePostfixExpressionInternal(e)) + ast->sub_expressions = snoc(ast->sub_expressions, e, _M_pool); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case Token_typename: { + std::size_t token = token_stream.cursor(); + token_stream.nextToken(); + + NameAST* name = 0; + if (!parseName(name, true)) + return false; + + CHECK('('); + ExpressionAST *expr = 0; + parseCommaExpression(expr); + CHECK(')'); + + TypeIdentificationAST *ast = CreateNode(_M_pool); + ast->typename_token = token; + ast->name = name; + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case Token_typeid: { + token_stream.nextToken(); + + CHECK('('); + TypeIdAST *typeId = 0; + parseTypeId(typeId); + CHECK(')'); + + TypeIdentificationAST *ast = CreateNode(_M_pool); + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + default: + break; + } + + std::size_t saved_pos = token_stream.cursor(); + + TypeSpecifierAST *typeSpec = 0; + ExpressionAST *expr = 0; + + // let's try to parse a type + NameAST *name = 0; + if (parseName(name, true)) { + Q_ASSERT(name->unqualified_name); + + bool has_template_args = name->unqualified_name->template_arguments != 0; + + if (has_template_args && token_stream.lookAhead() == '(') { + ExpressionAST *cast_expr = 0; + if (parseCastExpression(cast_expr) + && cast_expr->kind == AST::Kind_CastExpression) { + token_stream.rewind((int) saved_pos); + parsePrimaryExpression(expr); + goto L_no_rewind; + } + } + } + + token_stream.rewind((int) saved_pos); + +L_no_rewind: + if (!expr && parseSimpleTypeSpecifier(typeSpec) + && token_stream.lookAhead() == '(') { + token_stream.nextToken(); // skip '(' + parseCommaExpression(expr); + CHECK(')'); + } else if (expr) { + typeSpec = 0; + } else { + typeSpec = 0; + token_stream.rewind((int) start); + + if (!parsePrimaryExpression(expr)) + return false; + } + + const ListNode *sub_expressions = 0; + ExpressionAST *sub_expression = 0; + + while (parsePostfixExpressionInternal(sub_expression)) + sub_expressions = snoc(sub_expressions, sub_expression, _M_pool); + + if (sub_expressions || !expr || (typeSpec && expr)) { + PostfixExpressionAST *ast = CreateNode(_M_pool); + ast->type_specifier = typeSpec; + ast->expression = expr; + ast->sub_expressions = sub_expressions; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } else + node = expr; + + return true; +} + +bool Parser::parseUnaryExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + switch (token_stream.lookAhead()) { + case Token_incr: + case Token_decr: + case '*': + case '&': + case '+': + case '-': + case '!': + case '~': { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *expr = 0; + if (!parseCastExpression(expr)) + return false; + + UnaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->expression = expr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + return true; + + case Token_sizeof: { + std::size_t sizeof_token = token_stream.cursor(); + token_stream.nextToken(); + + SizeofExpressionAST *ast = CreateNode(_M_pool); + ast->sizeof_token = sizeof_token; + + std::size_t index = token_stream.cursor(); + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + if (parseTypeId(ast->type_id) && token_stream.lookAhead() == ')') { + token_stream.nextToken(); // skip ) + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + return true; + } + + ast->type_id = 0; + token_stream.rewind((int) index); + } + + if (!parseUnaryExpression(ast->expression)) + return false; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + return true; + } + + default: + break; + } + + int token = token_stream.lookAhead(); + + if (token == Token_new + || (token == Token_scope && token_stream.lookAhead(1) == Token_new)) + return parseNewExpression(node); + + if (token == Token_delete + || (token == Token_scope && token_stream.lookAhead(1) == Token_delete)) + return parseDeleteExpression(node); + + return parsePostfixExpression(node); +} + +bool Parser::parseNewExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + NewExpressionAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_scope + && token_stream.lookAhead(1) == Token_new) { + ast->scope_token = token_stream.cursor(); + token_stream.nextToken(); + } + + CHECK(Token_new); + ast->new_token = token_stream.cursor() - 1; + + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + parseCommaExpression(ast->expression); + CHECK(')'); + } + + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + parseTypeId(ast->type_id); + CHECK(')'); + } else { + parseNewTypeId(ast->new_type_id); + } + + parseNewInitializer(ast->new_initializer); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseNewTypeId(NewTypeIdAST *&node) +{ + std::size_t start = token_stream.cursor(); + + TypeSpecifierAST *typeSpec = 0; + if (!parseTypeSpecifier(typeSpec)) + return false; + + NewTypeIdAST *ast = CreateNode(_M_pool); + ast->type_specifier = typeSpec; + + parseNewDeclarator(ast->new_declarator); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseNewDeclarator(NewDeclaratorAST *&node) +{ + std::size_t start = token_stream.cursor(); + + NewDeclaratorAST *ast = CreateNode(_M_pool); + + PtrOperatorAST *ptrOp = 0; + if (parsePtrOperator(ptrOp)) { + ast->ptr_op = ptrOp; + parseNewDeclarator(ast->sub_declarator); + } + + while (token_stream.lookAhead() == '[') { + token_stream.nextToken(); + ExpressionAST *expr = 0; + parseExpression(expr); + ast->expressions = snoc(ast->expressions, expr, _M_pool); + ADVANCE(']', "]"); + } + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseNewInitializer(NewInitializerAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK('('); + + NewInitializerAST *ast = CreateNode(_M_pool); + + parseCommaExpression(ast->expression); + + CHECK(')'); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseDeleteExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + DeleteExpressionAST *ast = CreateNode(_M_pool); + + if (token_stream.lookAhead() == Token_scope + && token_stream.lookAhead(1) == Token_delete) { + ast->scope_token = token_stream.cursor(); + token_stream.nextToken(); + } + + CHECK(Token_delete); + ast->delete_token = token_stream.cursor() - 1; + + if (token_stream.lookAhead() == '[') { + ast->lbracket_token = token_stream.cursor(); + token_stream.nextToken(); + CHECK(']'); + ast->rbracket_token = token_stream.cursor() - 1; + } + + if (!parseCastExpression(ast->expression)) + return false; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseCastExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (token_stream.lookAhead() == '(') { + token_stream.nextToken(); + + CastExpressionAST *ast = CreateNode(_M_pool); + + if (parseTypeId(ast->type_id)) { + if (token_stream.lookAhead() == ')') { + token_stream.nextToken(); + + if (parseCastExpression(ast->expression)) { + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; + } + } + } + } + + token_stream.rewind((int) start); + return parseUnaryExpression(node); +} + +bool Parser::parsePmExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (!parseCastExpression(node) || !node) // ### fixme + return false; + + while (token_stream.lookAhead() == Token_ptrmem) { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseCastExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseMultiplicativeExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (!parsePmExpression(node)) + return false; + + while (token_stream.lookAhead() == '*' + || token_stream.lookAhead() == '/' + || token_stream.lookAhead() == '%') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parsePmExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + + +bool Parser::parseAdditiveExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (!parseMultiplicativeExpression(node)) + return false; + + while (token_stream.lookAhead() == '+' || token_stream.lookAhead() == '-') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseMultiplicativeExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseShiftExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (!parseAdditiveExpression(node)) + return false; + + while (token_stream.lookAhead() == Token_shift) { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseAdditiveExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseRelationalExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseShiftExpression(node)) + return false; + + while (token_stream.lookAhead() == '<' + || (token_stream.lookAhead() == '>' && !templArgs) + || token_stream.lookAhead() == Token_leq + || token_stream.lookAhead() == Token_geq) { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseShiftExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseEqualityExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseRelationalExpression(node, templArgs)) + return false; + + while (token_stream.lookAhead() == Token_eq + || token_stream.lookAhead() == Token_not_eq) { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseRelationalExpression(rightExpr, templArgs)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseAndExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseEqualityExpression(node, templArgs)) + return false; + + while (token_stream.lookAhead() == '&') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseEqualityExpression(rightExpr, templArgs)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseExclusiveOrExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseAndExpression(node, templArgs)) + return false; + + while (token_stream.lookAhead() == '^') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseAndExpression(rightExpr, templArgs)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseInclusiveOrExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseExclusiveOrExpression(node, templArgs)) + return false; + + while (token_stream.lookAhead() == '|') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseExclusiveOrExpression(rightExpr, templArgs)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseLogicalAndExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseInclusiveOrExpression(node, templArgs)) + return false; + + while (token_stream.lookAhead() == Token_and) { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseInclusiveOrExpression(rightExpr, templArgs)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseLogicalOrExpression(ExpressionAST *&node, bool templArgs) +{ + std::size_t start = token_stream.cursor(); + + if (!parseLogicalAndExpression(node, templArgs)) + return false; + + while (token_stream.lookAhead() == Token_or) { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseLogicalAndExpression(rightExpr, templArgs)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseConditionalExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (!parseLogicalOrExpression(node)) + return false; + + if (token_stream.lookAhead() == '?') { + token_stream.nextToken(); + + ExpressionAST *leftExpr = 0; + if (!parseExpression(leftExpr)) + return false; + + CHECK(':'); + + ExpressionAST *rightExpr = 0; + if (!parseAssignmentExpression(rightExpr)) + return false; + + ConditionalExpressionAST *ast + = CreateNode(_M_pool); + + ast->condition = node; + ast->left_expression = leftExpr; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseAssignmentExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (token_stream.lookAhead() == Token_throw && !parseThrowExpression(node)) + return false; + else if (!parseConditionalExpression(node)) + return false; + + while (token_stream.lookAhead() == Token_assign + || token_stream.lookAhead() == '=') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseConditionalExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseConstantExpression(ExpressionAST *&node) +{ + return parseConditionalExpression(node); +} + +bool Parser::parseExpression(ExpressionAST *&node) +{ + return parseCommaExpression(node); +} + +bool Parser::parseCommaExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + if (!parseAssignmentExpression(node)) + return false; + + while (token_stream.lookAhead() == ',') { + std::size_t op = token_stream.cursor(); + token_stream.nextToken(); + + ExpressionAST *rightExpr = 0; + if (!parseAssignmentExpression(rightExpr)) + return false; + + BinaryExpressionAST *ast = CreateNode(_M_pool); + ast->op = op; + ast->left_expression = node; + ast->right_expression = rightExpr; + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + } + + return true; +} + +bool Parser::parseThrowExpression(ExpressionAST *&node) +{ + std::size_t start = token_stream.cursor(); + + CHECK(Token_throw); + + ThrowExpressionAST *ast = CreateNode(_M_pool); + ast->throw_token = token_stream.cursor() - 1; + + parseAssignmentExpression(ast->expression); + + UPDATE_POS(ast, start, token_stream.cursor()); + node = ast; + + return true; +} + +bool Parser::parseQ_ENUMS(DeclarationAST *&node) +{ + if (token_stream.lookAhead() != Token_Q_ENUMS) + return false; + + if (token_stream.lookAhead(1) != '(') + return false; + + token_stream.nextToken(); + token_stream.nextToken(); + + int firstToken = token_stream.cursor(); + while (token_stream.lookAhead() != ')') + token_stream.nextToken(); + + QEnumsAST *ast = CreateNode(_M_pool); + UPDATE_POS(ast, firstToken, token_stream.cursor()); + node = ast; + + token_stream.nextToken(); + + return true; +} + +bool Parser::parseQ_PROPERTY(DeclarationAST *&node) +{ + if (token_stream.lookAhead() != Token_Q_PROPERTY) + return false; + + if (token_stream.lookAhead(1) != '(') + return false; + + token_stream.nextToken(); + token_stream.nextToken(); + + int firstToken = token_stream.cursor(); + while (token_stream.lookAhead() != ')') + token_stream.nextToken(); + + QPropertyAST *ast = CreateNode(_M_pool); + UPDATE_POS(ast, firstToken, token_stream.cursor()); + node = ast; + +// const Token &t1 = token_stream[firstToken]; +// const Token &t2 = token_stream[token_stream.cursor()]; +// printf("property: %s\n", +// qPrintable(QString::fromLatin1(t1.text + t1.position, t2.position - t1.position))); + + token_stream.nextToken(); + + return true; +} + +bool Parser::block_errors(bool block) +{ + bool current = _M_block_errors; + _M_block_errors = block; + return current; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/parser.h b/parser/parser.h new file mode 100644 index 000000000..4b0c76c85 --- /dev/null +++ b/parser/parser.h @@ -0,0 +1,198 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef PARSER_H +#define PARSER_H + +#include "ast.h" +#include "lexer.h" + +#include + +class FileSymbol; +class Control; + +class Parser +{ +public: + Parser(Control *control); + ~Parser(); + + LocationManager &location() { return _M_location; } + + TranslationUnitAST *parse(const char *contents, std::size_t size, pool *p); + +private: + void reportError(const QString& msg); + void syntaxError(); + void tokenRequiredError(int expected); + +public: + bool skipFunctionBody(StatementAST *&node); + +public: + bool parse_Attribute__(); + bool parseAbstractDeclarator(DeclaratorAST *&node); + bool parseAccessSpecifier(DeclarationAST *&node); + bool parseAdditiveExpression(ExpressionAST *&node); + bool parseAndExpression(ExpressionAST *&node, bool templArgs = false); + bool parseAsmDefinition(DeclarationAST *&node); + bool parseAssignmentExpression(ExpressionAST *&node); + bool parseBaseClause(BaseClauseAST *&node); + bool parseBaseSpecifier(BaseSpecifierAST *&node); + bool parseBlockDeclaration(DeclarationAST *&node); + bool parseCastExpression(ExpressionAST *&node); + bool parseClassSpecifier(TypeSpecifierAST *&node); + bool parseForwardDeclarationSpecifier(TypeSpecifierAST *&node); + bool parseCommaExpression(ExpressionAST *&node); + bool parseCompoundStatement(StatementAST *&node); + bool parseCondition(ConditionAST *&node, bool initRequired = true); + bool parseConditionalExpression(ExpressionAST *&node); + bool parseConstantExpression(ExpressionAST *&node); + bool parseCtorInitializer(CtorInitializerAST *&node); + bool parseCvQualify(const ListNode *&node); + bool parseDeclaration(DeclarationAST *&node); + bool parseDeclarationInternal(DeclarationAST *&node); + bool parseDeclarationStatement(StatementAST *&node); + bool parseDeclarator(DeclaratorAST *&node); + bool parseDeleteExpression(ExpressionAST *&node); + bool parseDoStatement(StatementAST *&node); + bool parseElaboratedTypeSpecifier(TypeSpecifierAST *&node); + bool parseEnumSpecifier(TypeSpecifierAST *&node); + bool parseEnumerator(EnumeratorAST *&node); + bool parseEqualityExpression(ExpressionAST *&node, + bool templArgs = false); + bool parseExceptionSpecification(ExceptionSpecificationAST *&node); + bool parseExclusiveOrExpression(ExpressionAST *&node, + bool templArgs = false); + bool parseExpression(ExpressionAST *&node); + bool parseExpressionOrDeclarationStatement(StatementAST *&node); + bool parseExpressionStatement(StatementAST *&node); + bool parseForInitStatement(StatementAST *&node); + bool parseForStatement(StatementAST *&node); + bool parseFunctionBody(StatementAST *&node); + bool parseFunctionSpecifier(const ListNode *&node); + bool parseIfStatement(StatementAST *&node); + bool parseInclusiveOrExpression(ExpressionAST *&node, + bool templArgs = false); + bool parseInitDeclarator(InitDeclaratorAST *&node); + bool parseInitDeclaratorList(const ListNode *&node); + bool parseInitializer(InitializerAST *&node); + bool parseInitializerClause(InitializerClauseAST *&node); + bool parseLabeledStatement(StatementAST *&node); + bool parseLinkageBody(LinkageBodyAST *&node); + bool parseLinkageSpecification(DeclarationAST *&node); + bool parseLogicalAndExpression(ExpressionAST *&node, + bool templArgs = false); + bool parseLogicalOrExpression(ExpressionAST *&node, + bool templArgs = false); + bool parseMemInitializer(MemInitializerAST *&node); + bool parseMemInitializerList(const ListNode *&node); + bool parseMemberSpecification(DeclarationAST *&node); + bool parseMultiplicativeExpression(ExpressionAST *&node); + bool parseName(NameAST *&node, bool acceptTemplateId = false); + bool parseNamespace(DeclarationAST *&node); + bool parseNamespaceAliasDefinition(DeclarationAST *&node); + bool parseNewDeclarator(NewDeclaratorAST *&node); + bool parseNewExpression(ExpressionAST *&node); + bool parseNewInitializer(NewInitializerAST *&node); + bool parseNewTypeId(NewTypeIdAST *&node); + bool parseOperator(OperatorAST *&node); + bool parseOperatorFunctionId(OperatorFunctionIdAST *&node); + bool parseParameterDeclaration(ParameterDeclarationAST *&node); + bool parseParameterDeclarationClause(ParameterDeclarationClauseAST *&node); + bool parseParameterDeclarationList(const ListNode *&node); + bool parsePmExpression(ExpressionAST *&node); + bool parsePostfixExpression(ExpressionAST *&node); + bool parsePostfixExpressionInternal(ExpressionAST *&node); + bool parsePrimaryExpression(ExpressionAST *&node); + bool parsePtrOperator(PtrOperatorAST *&node); + bool parsePtrToMember(PtrToMemberAST *&node); + bool parseRelationalExpression(ExpressionAST *&node, + bool templArgs = false); + bool parseShiftExpression(ExpressionAST *&node); + bool parseSimpleTypeSpecifier(TypeSpecifierAST *&node, + bool onlyIntegral = false); + bool parseStatement(StatementAST *&node); + bool parseStorageClassSpecifier(const ListNode *&node); + bool parseStringLiteral(StringLiteralAST *&node); + bool parseSwitchStatement(StatementAST *&node); + bool parseTemplateArgument(TemplateArgumentAST *&node); + bool parseTemplateArgumentList(const ListNode *&node, + bool reportError = true); + bool parseTemplateDeclaration(DeclarationAST *&node); + bool parseTemplateParameter(TemplateParameterAST *&node); + bool parseTemplateParameterList(const ListNode *&node); + bool parseThrowExpression(ExpressionAST *&node); + bool parseTranslationUnit(TranslationUnitAST *&node); + bool parseTryBlockStatement(StatementAST *&node); + bool parseTypeId(TypeIdAST *&node); + bool parseTypeIdList(const ListNode *&node); + bool parseTypeParameter(TypeParameterAST *&node); + bool parseTypeSpecifier(TypeSpecifierAST *&node); + bool parseTypeSpecifierOrClassSpec(TypeSpecifierAST *&node); + bool parseTypedef(DeclarationAST *&node); + bool parseUnaryExpression(ExpressionAST *&node); + bool parseUnqualifiedName(UnqualifiedNameAST *&node, + bool parseTemplateId = true); + bool parseUsing(DeclarationAST *&node); + bool parseUsingDirective(DeclarationAST *&node); + bool parseWhileStatement(StatementAST *&node); + bool parseWinDeclSpec(WinDeclSpecAST *&node); + + bool parseQ_PROPERTY(DeclarationAST *&node); + bool parseQ_ENUMS(DeclarationAST *&node); + + bool skipUntil(int token); + bool skipUntilDeclaration(); + bool skipUntilStatement(); + bool skip(int l, int r); + + void advance(); + + // private: + TokenStream token_stream; + LocationTable location_table; + LocationTable line_table; + + bool block_errors(bool block); + +private: + QString tokenText(AST *) const; + + LocationManager _M_location; + Control *control; + Lexer lexer; + pool *_M_pool; + bool _M_block_errors; + +private: + Parser(const Parser& source); + void operator = (const Parser& source); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/r++.macros b/parser/r++.macros new file mode 100644 index 000000000..455276c84 --- /dev/null +++ b/parser/r++.macros @@ -0,0 +1,28 @@ + +#define __attribute__(a...) +#define __typeof__ __typeof + +#define __extension +#define __extension__ + +#define __restrict +#define __restrict__ + +#define __volatile volatile +#define __volatile__ volatile + +#define __inline inline +#define __inline__ inline + +#define __const const +#define __const__ const + +#define __asm asm +#define __asm__ asm + +#define __GNUC__ 3 +//#define __GNUC_MINOR__ 4 + +#define __ROBC__ 0 +#define __ROBC_MINOR__ 1 + diff --git a/parser/rpp-allocator.h b/parser/rpp-allocator.h new file mode 100644 index 000000000..313895f00 --- /dev/null +++ b/parser/rpp-allocator.h @@ -0,0 +1,24 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "parser/rxx_allocator.h" diff --git a/parser/rpp/builtin-macros.cpp b/parser/rpp/builtin-macros.cpp new file mode 100644 index 000000000..1b0fc06af --- /dev/null +++ b/parser/rpp/builtin-macros.cpp @@ -0,0 +1,23 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + diff --git a/parser/rpp/pp-cctype.h b/parser/rpp/pp-cctype.h new file mode 100644 index 000000000..5b0bf477c --- /dev/null +++ b/parser/rpp/pp-cctype.h @@ -0,0 +1,57 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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/parser/rpp/pp-configuration b/parser/rpp/pp-configuration new file mode 100644 index 000000000..15586dd88 --- /dev/null +++ b/parser/rpp/pp-configuration @@ -0,0 +1,86 @@ +#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/parser/rpp/pp-engine-bits.h b/parser/rpp/pp-engine-bits.h new file mode 100644 index 000000000..7594ab796 --- /dev/null +++ b/parser/rpp/pp-engine-bits.h @@ -0,0 +1,1252 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_ENGINE_BITS_H +#define PP_ENGINE_BITS_H + +#include "pp.h" +#include +#include +#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 = std::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 (!strcmp(__directive, "warning")) + return PP_WARNING; + break; + + case 12: + if (__directive[0] == 'i' && !strcmp(__directive, "include_next")) + return PP_INCLUDE_NEXT; + break; + + default: + break; + } + 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 std::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 std::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 std::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) +{ + 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()); + 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) +{ + 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.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.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.variadics = true; + while (*__first == '.') + ++__first; + } + } + + assert(*__first == ')'); + ++__first; + } + + __first = skip_blanks(__first, __last); + + while (__first != __last && *__first != '\n') { + if (*__first == '/') { + __first = skip_comment_or_divop(__first, __last); + env.current_line += skip_comment_or_divop.lines; + } + + 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) << 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 ``)''" << 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 << 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" << std::endl; + result->set_long(0); + } else + result->op_div(value); + } else { + if (value.is_zero()) { + std::cerr << "** WARNING division by zero" << 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) << 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 +_InputIterator pp::handle_if(_InputIterator __first, _InputIterator __last) +{ + if (test_if_level()) { + pp_macro_expander expand_condition(env); + std::string condition; + condition.reserve(255); + expand_condition(skip_blanks(__first, __last), __last, std::back_inserter(condition)); + + 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" << 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" << std::endl; + } else if (!_M_true_test[iflevel] && !_M_skipping[iflevel - 1]) { + Value result; + __first = eval_expression(__first, __last, &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" << 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/parser/rpp/pp-engine.h b/parser/rpp/pp-engine.h new file mode 100644 index 000000000..79e74c1aa --- /dev/null +++ b/parser/rpp/pp-engine.h @@ -0,0 +1,274 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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_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 + 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 + inline 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/parser/rpp/pp-environment.h b/parser/rpp/pp-environment.h new file mode 100644 index 000000000..6003c0820 --- /dev/null +++ b/parser/rpp/pp-environment.h @@ -0,0 +1,137 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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->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->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/parser/rpp/pp-fwd.h b/parser/rpp/pp-fwd.h new file mode 100644 index 000000000..5b1c792f8 --- /dev/null +++ b/parser/rpp/pp-fwd.h @@ -0,0 +1,39 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_FWD_H +#define PP_FWD_H + +namespace rpp +{ + +template class pp_string; + +typedef pp_string pp_fast_string; + +} // namespace rpp + +#endif // PP_FWD_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/rpp/pp-internal.h b/parser/rpp/pp-internal.h new file mode 100644 index 000000000..7e708101d --- /dev/null +++ b/parser/rpp/pp-internal.h @@ -0,0 +1,117 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_INTERNAL_H +#define PP_INTERNAL_H + +#include +#include +#include "pp.h" + +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: public std::binary_function { + inline bool operator()(pp_fast_string const *__lhs, pp_fast_string const *__rhs) const { + return *__lhs < *__rhs; + } +}; + +struct _Equal_to_string: public std::binary_function { + inline bool operator()(pp_fast_string const *__lhs, pp_fast_string const *__rhs) const { + return *__lhs == *__rhs; + } +}; + +struct _Hash_string: public std::unary_function { + 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/parser/rpp/pp-iterator.h b/parser/rpp/pp-iterator.h new file mode 100644 index 000000000..fe846bd26 --- /dev/null +++ b/parser/rpp/pp-iterator.h @@ -0,0 +1,88 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_ITERATOR_H +#define PP_ITERATOR_H + +#include + +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) {} + + 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/parser/rpp/pp-macro-expander.h b/parser/rpp/pp-macro-expander.h new file mode 100644 index 000000000..fd875d980 --- /dev/null +++ b/parser/rpp/pp-macro-expander.h @@ -0,0 +1,351 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_MACRO_EXPANDER_H +#define PP_MACRO_EXPANDER_H + +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) { + assert(__name != 0); + + if (! frame) + return 0; + + assert(frame->expanding_macro != 0); + + std::vector const formals = frame->expanding_macro->formals; + 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 + 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->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->function_like) { + pp_macro *m = 0; + + if (macro->definition) { + macro->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->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->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->hidden = true; + expand_macro(macro->definition->begin(), macro->definition->end(), __result); + macro->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->variadics && __first != arg_end && arg_end != __last && *arg_end == ',' + && (__actuals.size() + 1) == __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/parser/rpp/pp-macro.h b/parser/rpp/pp-macro.h new file mode 100644 index 000000000..e633834b1 --- /dev/null +++ b/parser/rpp/pp-macro.h @@ -0,0 +1,72 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_MACRO_H +#define PP_MACRO_H + +#include +#include "pp-fwd.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; + }; + }; + + 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/parser/rpp/pp-main.cpp b/parser/rpp/pp-main.cpp new file mode 100644 index 000000000..4c37e754f --- /dev/null +++ b/parser/rpp/pp-main.cpp @@ -0,0 +1,295 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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; + } +} +#if 0 +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; +} +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/rpp/pp-qt-configuration b/parser/rpp/pp-qt-configuration new file mode 100644 index 000000000..206c3d643 --- /dev/null +++ b/parser/rpp/pp-qt-configuration @@ -0,0 +1,24 @@ +#define __cplusplus 1 + +#define __STDC__ + +// Qt +#define QOBJECTDEFS_H + +// not yet supported +#define Q_SLOTS slots +#define Q_SIGNALS signals +#define Q_FLAGS(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 QT_NO_QOBJECT_CHECK +#define QT_NO_MEMBER_TEMPLATES +// There are symbols in Qt that exist in Debug but +// not in release +#define QT_NO_DEBUG + diff --git a/parser/rpp/pp-scanner.h b/parser/rpp/pp-scanner.h new file mode 100644 index 000000000..e97b15843 --- /dev/null +++ b/parser/rpp/pp-scanner.h @@ -0,0 +1,313 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_SCANNER_H +#define PP_SCANNER_H + +#include "pp-cctype.h" +#include + +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 { + 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 + return __first; + 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; + } +}; + +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/parser/rpp/pp-string.h b/parser/rpp/pp-string.h new file mode 100644 index 000000000..31bca4f7b --- /dev/null +++ b/parser/rpp/pp-string.h @@ -0,0 +1,107 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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/parser/rpp/pp-symbol.h b/parser/rpp/pp-symbol.h new file mode 100644 index 000000000..eef668379 --- /dev/null +++ b/parser/rpp/pp-symbol.h @@ -0,0 +1,87 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef PP_SYMBOL_H +#define PP_SYMBOL_H + +#include +#include +#include "pp-fwd.h" +#include "parser/rxx_allocator.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/parser/rpp/pp.h b/parser/rpp/pp.h new file mode 100644 index 000000000..3ff38c0d1 --- /dev/null +++ b/parser/rpp/pp.h @@ -0,0 +1,91 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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 + +#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 "parser/rxx_allocator.h" +#else +# include "parser/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/parser/rpp/preprocessor.cpp b/parser/rpp/preprocessor.cpp new file mode 100644 index 000000000..be6a85186 --- /dev/null +++ b/parser/rpp/preprocessor.cpp @@ -0,0 +1,158 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Harald Fernengel + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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) { + foreach(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->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/parser/rpp/preprocessor.h b/parser/rpp/preprocessor.h new file mode 100644 index 000000000..c363ea7c8 --- /dev/null +++ b/parser/rpp/preprocessor.h @@ -0,0 +1,66 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright 2005 Harald Fernengel + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#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/parser/rxx_allocator.h b/parser/rxx_allocator.h new file mode 100644 index 000000000..e4a387554 --- /dev/null +++ b/parser/rxx_allocator.h @@ -0,0 +1,130 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef RXX_ALLOCATOR_H +#define RXX_ALLOCATOR_H + +#include +#include +#include +#include + +template class rxx_allocator +{ +public: + typedef _Tp value_type; + typedef _Tp* pointer; + typedef const _Tp* const_pointer; + typedef _Tp& reference; + typedef const _Tp& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static const size_type max_block_count = size_type(-1); + static const size_type _S_block_size = 1 << 16; // 64K + + rxx_allocator() + { + _M_block_index = max_block_count; + _M_current_index = 0; + _M_storage = 0; + _M_current_block = 0; + } + + ~rxx_allocator() + { + for (size_type index = 0; index < _M_block_index + 1; ++index) + delete[] _M_storage[index]; + + ::free(_M_storage); + } + + pointer address(reference __val) { return &__val; } + const_pointer address(const_reference __val) const { return &__val; } + + pointer allocate(size_type __n, const void* = 0) + { + const size_type bytes = __n * sizeof(_Tp); + + if (_M_current_block == 0 + || _S_block_size < _M_current_index + bytes) { + ++_M_block_index; + + _M_storage = reinterpret_cast + (::realloc(_M_storage, sizeof(char*) * (1 + _M_block_index))); + + _M_current_block = _M_storage[_M_block_index] = reinterpret_cast + (new char[_S_block_size]); + +#if defined(RXX_ALLOCATOR_INIT_0) // ### make it a policy + ::memset(_M_current_block, 0, _S_block_size); +#endif + _M_current_index = 0; + } + + pointer p = reinterpret_cast + (_M_current_block + _M_current_index); + + _M_current_index += bytes; + + return p; + } + + void deallocate(pointer __p, size_type __n) {} + + size_type max_size() const + { + return size_type(-1) / sizeof(_Tp); + } + + void contruct(pointer __p, const_reference __val) + { + new(__p) _Tp(__val); + } + void destruct(pointer __p) + { + __p->~_Tp(); + } + +private: + template + struct rebind + { + typedef rxx_allocator<_Tp1> other; + }; + + template rxx_allocator(const rxx_allocator<_Tp1> &__o) {} + +private: + size_type _M_block_index; + size_type _M_current_index; + char *_M_current_block; + char **_M_storage; +}; + +#endif // RXX_ALLOCATOR_H + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/smallobject.cpp b/parser/smallobject.cpp new file mode 100644 index 000000000..e96d417c6 --- /dev/null +++ b/parser/smallobject.cpp @@ -0,0 +1,28 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "smallobject.h" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/smallobject.h b/parser/smallobject.h new file mode 100644 index 000000000..52cdc232f --- /dev/null +++ b/parser/smallobject.h @@ -0,0 +1,47 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef SMALLOBJECT_H +#define SMALLOBJECT_H + +#include "rxx_allocator.h" +#include + +class pool +{ + rxx_allocator __alloc; + +public: + inline void *allocate(std::size_t __size); +}; + +inline void *pool::allocate(std::size_t __size) +{ + return __alloc.allocate(__size); +} + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/symbol.h b/parser/symbol.h new file mode 100644 index 000000000..8e393cddb --- /dev/null +++ b/parser/symbol.h @@ -0,0 +1,122 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef SYMBOL_H +#define SYMBOL_H + +#include +#include + +#include +#include + +struct NameSymbol +{ + const char *data; + std::size_t count; + + inline QString as_string() const + { + return QString::fromUtf8(data, (int) count); + } + + inline bool operator == (const NameSymbol &other) const + { + return count == other.count + && !std::strncmp(data, other.data, count); + } + +protected: + inline NameSymbol() {} + inline NameSymbol(const char *d, std::size_t c) + : data(d), count(c) {} + +private: + void operator = (const NameSymbol &); + + friend class NameTable; +}; + +inline uint qHash(const NameSymbol &r) +{ + uint hash_value = 0; + + for (std::size_t i = 0; i < r.count; ++i) + hash_value = (hash_value << 5) - hash_value + r.data[i]; + + return hash_value; +} + +inline uint qHash(const QPair &r) +{ + uint hash_value = 0; + + for (std::size_t i = 0; i < r.second; ++i) + hash_value = (hash_value << 5) - hash_value + r.first[i]; + + return hash_value; +} + +class NameTable +{ +public: + typedef QPair KeyType; + typedef QHash ContainerType; + +public: + NameTable() {} + + ~NameTable() + { + qDeleteAll(_M_storage); + } + + inline const NameSymbol *findOrInsert(const char *str, std::size_t len) + { + KeyType key(str, len); + + NameSymbol *name = _M_storage.value(key); + if (!name) { + name = new NameSymbol(str, len); + _M_storage.insert(key, name); + } + + return name; + } + + inline std::size_t count() const { return _M_storage.size(); } + +private: + ContainerType _M_storage; + +private: + NameTable(const NameTable &other); + void operator=(const NameTable &other); +}; + +#endif // SYMBOL_H + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/tokens.cpp b/parser/tokens.cpp new file mode 100644 index 000000000..2e054d894 --- /dev/null +++ b/parser/tokens.cpp @@ -0,0 +1,249 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include + +#include "tokens.h" + +static char const * const _S_token_names[] = { + "K_DCOP", + "Q_OBJECT", + "Q_PROPERTY", + "__attribute__", + "__typeof", + "and", + "and_eq", + "arrow", + "asm", + "assign", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "char_literal", + "class", + "comment", + "compl", + "concat", + "const", + "const_cast", + "continue", + "decr", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "ellipsis", + "else", + "emit", + "enum", + "eq", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "geq", + "goto", + "identifier", + "if", + "incr", + "inline", + "int", + "k_dcop", + "k_dcop_signals", + "leq", + "long", + "mutable", + "namespace", + "new", + "not", + "not_eq", + "number_literal", + "operator", + "or", + "or_eq", + "preproc", + "private", + "protected", + "ptrmem", + "public", + "register", + "reinterpret_cast", + "return", + "scope", + "shift", + "short", + "signals", + "signed", + "sizeof", + "slots", + "static", + "static_cast", + "string_literal", + "struct", + "switch", + "template", + "this", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "whitespaces", + "xor", + "xor_eq", + "Q_ENUMS" +}; + +static char _S_printable[][2] = { + { char(32), '\0' }, + { char(33), '\0' }, + { char(34), '\0' }, + { char(35), '\0' }, + { char(36), '\0' }, + { char(37), '\0' }, + { char(38), '\0' }, + { char(39), '\0' }, + { char(40), '\0' }, + { char(41), '\0' }, + { char(42), '\0' }, + { char(43), '\0' }, + { char(44), '\0' }, + { char(45), '\0' }, + { char(46), '\0' }, + { char(47), '\0' }, + { char(48), '\0' }, + { char(49), '\0' }, + { char(50), '\0' }, + { char(51), '\0' }, + { char(52), '\0' }, + { char(53), '\0' }, + { char(54), '\0' }, + { char(55), '\0' }, + { char(56), '\0' }, + { char(57), '\0' }, + { char(58), '\0' }, + { char(59), '\0' }, + { char(60), '\0' }, + { char(61), '\0' }, + { char(62), '\0' }, + { char(63), '\0' }, + { char(64), '\0' }, + { char(65), '\0' }, + { char(66), '\0' }, + { char(67), '\0' }, + { char(68), '\0' }, + { char(69), '\0' }, + { char(70), '\0' }, + { char(71), '\0' }, + { char(72), '\0' }, + { char(73), '\0' }, + { char(74), '\0' }, + { char(75), '\0' }, + { char(76), '\0' }, + { char(77), '\0' }, + { char(78), '\0' }, + { char(79), '\0' }, + { char(80), '\0' }, + { char(81), '\0' }, + { char(82), '\0' }, + { char(83), '\0' }, + { char(84), '\0' }, + { char(85), '\0' }, + { char(86), '\0' }, + { char(87), '\0' }, + { char(88), '\0' }, + { char(89), '\0' }, + { char(90), '\0' }, + { char(91), '\0' }, + { char(92), '\0' }, + { char(93), '\0' }, + { char(94), '\0' }, + { char(95), '\0' }, + { char(96), '\0' }, + { char(97), '\0' }, + { char(98), '\0' }, + { char(99), '\0' }, + { char(100), '\0' }, + { char(101), '\0' }, + { char(102), '\0' }, + { char(103), '\0' }, + { char(104), '\0' }, + { char(105), '\0' }, + { char(106), '\0' }, + { char(107), '\0' }, + { char(108), '\0' }, + { char(109), '\0' }, + { char(110), '\0' }, + { char(111), '\0' }, + { char(112), '\0' }, + { char(113), '\0' }, + { char(114), '\0' }, + { char(115), '\0' }, + { char(116), '\0' }, + { char(117), '\0' }, + { char(118), '\0' }, + { char(119), '\0' }, + { char(120), '\0' }, + { char(121), '\0' }, + { char(122), '\0' }, + { char(123), '\0' }, + { char(124), '\0' }, + { char(125), '\0' }, + { char(126), '\0' }, + { char(127), '\0' }, +}; + +char const *token_name(int token) +{ + if (token == 0) + return "eof"; + else if (token >= 32 && token <= 127) + return _S_printable[token - 32]; + else if (token >= 1000) + return _S_token_names[token - 1000]; + + Q_ASSERT(0); + return 0; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/tokens.h b/parser/tokens.h new file mode 100644 index 000000000..257ff0992 --- /dev/null +++ b/parser/tokens.h @@ -0,0 +1,145 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef TOKENS_H +#define TOKENS_H + +enum TOKEN_KIND { + Token_EOF = 0, + + Token_K_DCOP = 1000, + Token_Q_OBJECT, + Token_Q_PROPERTY, + Token___attribute__, + Token___typeof, + Token_and, + Token_and_eq, + Token_arrow, + Token_asm, + Token_assign, + Token_auto, + Token_bitand, + Token_bitor, + Token_bool, + Token_break, + Token_case, + Token_catch, + Token_char, + Token_char_literal, + Token_class, + Token_comment, + Token_compl, + Token_concat, + Token_const, + Token_const_cast, + Token_continue, + Token_decr, + Token_default, + Token_delete, + Token_do, + Token_double, + Token_dynamic_cast, + Token_ellipsis, + Token_else, + Token_emit, + Token_enum, + Token_eq, + Token_explicit, + Token_export, + Token_extern, + Token_false, + Token_float, + Token_for, + Token_friend, + Token_geq, + Token_goto, + Token_identifier, + Token_if, + Token_incr, + Token_inline, + Token_int, + Token_k_dcop, + Token_k_dcop_signals, + Token_leq, + Token_long, + Token_mutable, + Token_namespace, + Token_new, + Token_not, + Token_not_eq, + Token_number_literal, + Token_operator, + Token_or, + Token_or_eq, + Token_preproc, + Token_private, + Token_protected, + Token_ptrmem, + Token_public, + Token_register, + Token_reinterpret_cast, + Token_return, + Token_scope, + Token_shift, + Token_short, + Token_signals, + Token_signed, + Token_sizeof, + Token_slots, + Token_static, + Token_static_cast, + Token_string_literal, + Token_struct, + Token_switch, + Token_template, + Token_this, + Token_throw, + Token_true, + Token_try, + Token_typedef, + Token_typeid, + Token_typename, + Token_union, + Token_unsigned, + Token_using, + Token_virtual, + Token_void, + Token_volatile, + Token_wchar_t, + Token_while, + Token_whitespaces, + Token_xor, + Token_xor_eq, + Token_Q_ENUMS, + Token_Q_INVOKABLE, + + TOKEN_KIND_COUNT +}; + +char const *token_name(int token); + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/type_compiler.cpp b/parser/type_compiler.cpp new file mode 100644 index 000000000..4bcdb277a --- /dev/null +++ b/parser/type_compiler.cpp @@ -0,0 +1,129 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + + +#include "type_compiler.h" +#include "name_compiler.h" +#include "lexer.h" +#include "symbol.h" +#include "tokens.h" +#include "binder.h" + +#include + +TypeCompiler::TypeCompiler(Binder *binder) + : _M_binder(binder), _M_token_stream(binder->tokenStream()) +{ +} + +void TypeCompiler::run(TypeSpecifierAST *node) +{ + _M_type.clear(); + _M_cv.clear(); + + visit(node); + + if (node && node->cv) { + const ListNode *it = node->cv->toFront(); + const ListNode *end = it; + do { + int kind = _M_token_stream->kind(it->element); + if (!_M_cv.contains(kind)) + _M_cv.append(kind); + + it = it->next; + } while (it != end); + } +} + +void TypeCompiler::visitClassSpecifier(ClassSpecifierAST *node) +{ + visit(node->name); +} + +void TypeCompiler::visitEnumSpecifier(EnumSpecifierAST *node) +{ + visit(node->name); +} + +void TypeCompiler::visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *node) +{ + visit(node->name); +} + +void TypeCompiler::visitSimpleTypeSpecifier(SimpleTypeSpecifierAST *node) +{ + if (const ListNode *it = node->integrals) { + it = it->toFront(); + const ListNode *end = it; + QString current_item; + do { + std::size_t token = it->element; + current_item += token_name(_M_token_stream->kind(token)); + current_item += " "; + it = it->next; + } while (it != end); + _M_type += current_item.trimmed(); + } else if (node->type_of) { + // ### implement me + _M_type += QLatin1String("typeof<...>"); + } + + visit(node->name); +} + +void TypeCompiler::visitName(NameAST *node) +{ + NameCompiler name_cc(_M_binder); + name_cc.run(node); + _M_type = name_cc.qualifiedName(); +} + +QStringList TypeCompiler::cvString() const +{ + QStringList lst; + + foreach (int q, cv()) { + if (q == Token_const) + lst.append(QLatin1String("const")); + else if (q == Token_volatile) + lst.append(QLatin1String("volatile")); + } + + return lst; +} + +bool TypeCompiler::isConstant() const +{ + return _M_cv.contains(Token_const); +} + +bool TypeCompiler::isVolatile() const +{ + return _M_cv.contains(Token_volatile); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/type_compiler.h b/parser/type_compiler.h new file mode 100644 index 000000000..985cc8e2e --- /dev/null +++ b/parser/type_compiler.h @@ -0,0 +1,71 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef TYPE_COMPILER_H +#define TYPE_COMPILER_H + +#include "default_visitor.h" + +#include +#include +#include + +class TokenStream; +class Binder; + +class TypeCompiler: protected DefaultVisitor +{ +public: + TypeCompiler(Binder *binder); + + inline QStringList qualifiedName() const { return _M_type; } + inline QList cv() const { return _M_cv; } + + bool isConstant() const; + bool isVolatile() const; + + QStringList cvString() const; + + void run(TypeSpecifierAST *node); + +protected: + virtual void visitClassSpecifier(ClassSpecifierAST *node); + virtual void visitEnumSpecifier(EnumSpecifierAST *node); + virtual void visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *node); + virtual void visitSimpleTypeSpecifier(SimpleTypeSpecifierAST *node); + + virtual void visitName(NameAST *node); + +private: + Binder *_M_binder; + TokenStream *_M_token_stream; + QStringList _M_type; + QList _M_cv; +}; + +#endif // TYPE_COMPILER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/parser/visitor.cpp b/parser/visitor.cpp new file mode 100644 index 000000000..742981414 --- /dev/null +++ b/parser/visitor.cpp @@ -0,0 +1,122 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#include "visitor.h" + +Visitor::visitor_fun_ptr Visitor::_S_table[AST::NODE_KIND_COUNT] = { + 0, + reinterpret_cast(&Visitor::visitAccessSpecifier), + reinterpret_cast(&Visitor::visitAsmDefinition), + reinterpret_cast(&Visitor::visitBaseClause), + reinterpret_cast(&Visitor::visitBaseSpecifier), + reinterpret_cast(&Visitor::visitBinaryExpression), + reinterpret_cast(&Visitor::visitCastExpression), + reinterpret_cast(&Visitor::visitClassMemberAccess), + reinterpret_cast(&Visitor::visitClassSpecifier), + reinterpret_cast(&Visitor::visitCompoundStatement), + reinterpret_cast(&Visitor::visitCondition), + reinterpret_cast(&Visitor::visitConditionalExpression), + reinterpret_cast(&Visitor::visitCppCastExpression), + reinterpret_cast(&Visitor::visitCtorInitializer), + reinterpret_cast(&Visitor::visitDeclarationStatement), + reinterpret_cast(&Visitor::visitDeclarator), + reinterpret_cast(&Visitor::visitDeleteExpression), + reinterpret_cast(&Visitor::visitDoStatement), + reinterpret_cast(&Visitor::visitElaboratedTypeSpecifier), + reinterpret_cast(&Visitor::visitEnumSpecifier), + reinterpret_cast(&Visitor::visitEnumerator), + reinterpret_cast(&Visitor::visitExceptionSpecification), + reinterpret_cast(&Visitor::visitExpressionOrDeclarationStatement), + reinterpret_cast(&Visitor::visitExpressionStatement), + reinterpret_cast(&Visitor::visitForStatement), + reinterpret_cast(&Visitor::visitFunctionCall), + reinterpret_cast(&Visitor::visitFunctionDefinition), + reinterpret_cast(&Visitor::visitIfStatement), + reinterpret_cast(&Visitor::visitIncrDecrExpression), + reinterpret_cast(&Visitor::visitInitDeclarator), + reinterpret_cast(&Visitor::visitInitializer), + reinterpret_cast(&Visitor::visitInitializerClause), + reinterpret_cast(&Visitor::visitLabeledStatement), + reinterpret_cast(&Visitor::visitLinkageBody), + reinterpret_cast(&Visitor::visitLinkageSpecification), + reinterpret_cast(&Visitor::visitMemInitializer), + reinterpret_cast(&Visitor::visitName), + reinterpret_cast(&Visitor::visitNamespace), + reinterpret_cast(&Visitor::visitNamespaceAliasDefinition), + reinterpret_cast(&Visitor::visitNewDeclarator), + reinterpret_cast(&Visitor::visitNewExpression), + reinterpret_cast(&Visitor::visitNewInitializer), + reinterpret_cast(&Visitor::visitNewTypeId), + reinterpret_cast(&Visitor::visitOperator), + reinterpret_cast(&Visitor::visitOperatorFunctionId), + reinterpret_cast(&Visitor::visitParameterDeclaration), + reinterpret_cast(&Visitor::visitParameterDeclarationClause), + reinterpret_cast(&Visitor::visitPostfixExpression), + reinterpret_cast(&Visitor::visitPrimaryExpression), + reinterpret_cast(&Visitor::visitPtrOperator), + reinterpret_cast(&Visitor::visitPtrToMember), + reinterpret_cast(&Visitor::visitReturnStatement), + reinterpret_cast(&Visitor::visitSimpleDeclaration), + reinterpret_cast(&Visitor::visitSimpleTypeSpecifier), + reinterpret_cast(&Visitor::visitSizeofExpression), + reinterpret_cast(&Visitor::visitStringLiteral), + reinterpret_cast(&Visitor::visitSubscriptExpression), + reinterpret_cast(&Visitor::visitSwitchStatement), + reinterpret_cast(&Visitor::visitTemplateArgument), + reinterpret_cast(&Visitor::visitTemplateDeclaration), + reinterpret_cast(&Visitor::visitTemplateParameter), + reinterpret_cast(&Visitor::visitThrowExpression), + reinterpret_cast(&Visitor::visitTranslationUnit), + reinterpret_cast(&Visitor::visitTryBlockStatement), + reinterpret_cast(&Visitor::visitTypeId), + reinterpret_cast(&Visitor::visitTypeIdentification), + reinterpret_cast(&Visitor::visitTypeParameter), + reinterpret_cast(&Visitor::visitTypedef), + reinterpret_cast(&Visitor::visitUnaryExpression), + reinterpret_cast(&Visitor::visitUnqualifiedName), + reinterpret_cast(&Visitor::visitUsing), + reinterpret_cast(&Visitor::visitUsingDirective), + reinterpret_cast(&Visitor::visitWhileStatement), + reinterpret_cast(&Visitor::visitWinDeclSpec), + reinterpret_cast(&Visitor::visitQProperty), + reinterpret_cast(&Visitor::visitForwardDeclarationSpecifier), + reinterpret_cast(&Visitor::visitQEnums) +}; + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +void Visitor::visit(AST *node) +{ + if (node) + (this->*_S_table[node->kind])(node); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/parser/visitor.h b/parser/visitor.h new file mode 100644 index 000000000..74edcca83 --- /dev/null +++ b/parser/visitor.h @@ -0,0 +1,140 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2002-2005 Roberto Raggi + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + + +#ifndef VISITOR_H +#define VISITOR_H + +#include "ast.h" + +class Visitor +{ +public: + Visitor(); + virtual ~Visitor(); + + virtual void visit(AST *node); + +protected: + virtual void visitAccessSpecifier(AccessSpecifierAST *) {} + virtual void visitAsmDefinition(AsmDefinitionAST *) {} + virtual void visitBaseClause(BaseClauseAST *) {} + virtual void visitBaseSpecifier(BaseSpecifierAST *) {} + virtual void visitBinaryExpression(BinaryExpressionAST *) {} + virtual void visitCastExpression(CastExpressionAST *) {} + virtual void visitClassMemberAccess(ClassMemberAccessAST *) {} + virtual void visitClassSpecifier(ClassSpecifierAST *) {} + virtual void visitCompoundStatement(CompoundStatementAST *) {} + virtual void visitCondition(ConditionAST *) {} + virtual void visitConditionalExpression(ConditionalExpressionAST *) {} + virtual void visitCppCastExpression(CppCastExpressionAST *) {} + virtual void visitCtorInitializer(CtorInitializerAST *) {} + virtual void visitDeclarationStatement(DeclarationStatementAST *) {} + virtual void visitDeclarator(DeclaratorAST *) {} + virtual void visitDeleteExpression(DeleteExpressionAST *) {} + virtual void visitDoStatement(DoStatementAST *) {} + virtual void visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *) {} + virtual void visitEnumSpecifier(EnumSpecifierAST *) {} + virtual void visitEnumerator(EnumeratorAST *) {} + virtual void visitExceptionSpecification(ExceptionSpecificationAST *) {} + virtual void visitExpressionOrDeclarationStatement(ExpressionOrDeclarationStatementAST *) {} + virtual void visitExpressionStatement(ExpressionStatementAST *) {} + virtual void visitForStatement(ForStatementAST *) {} + virtual void visitFunctionCall(FunctionCallAST *) {} + virtual void visitFunctionDefinition(FunctionDefinitionAST *) {} + virtual void visitIfStatement(IfStatementAST *) {} + virtual void visitIncrDecrExpression(IncrDecrExpressionAST *) {} + virtual void visitInitDeclarator(InitDeclaratorAST *) {} + virtual void visitInitializer(InitializerAST *) {} + virtual void visitInitializerClause(InitializerClauseAST *) {} + virtual void visitLabeledStatement(LabeledStatementAST *) {} + virtual void visitLinkageBody(LinkageBodyAST *) {} + virtual void visitLinkageSpecification(LinkageSpecificationAST *) {} + virtual void visitMemInitializer(MemInitializerAST *) {} + virtual void visitName(NameAST *) {} + virtual void visitNamespace(NamespaceAST *) {} + virtual void visitNamespaceAliasDefinition(NamespaceAliasDefinitionAST *) {} + virtual void visitNewDeclarator(NewDeclaratorAST *) {} + virtual void visitNewExpression(NewExpressionAST *) {} + virtual void visitNewInitializer(NewInitializerAST *) {} + virtual void visitNewTypeId(NewTypeIdAST *) {} + virtual void visitOperator(OperatorAST *) {} + virtual void visitOperatorFunctionId(OperatorFunctionIdAST *) {} + virtual void visitParameterDeclaration(ParameterDeclarationAST *) {} + virtual void visitParameterDeclarationClause(ParameterDeclarationClauseAST *) {} + virtual void visitPostfixExpression(PostfixExpressionAST *) {} + virtual void visitPrimaryExpression(PrimaryExpressionAST *) {} + virtual void visitPtrOperator(PtrOperatorAST *) {} + virtual void visitPtrToMember(PtrToMemberAST *) {} + virtual void visitReturnStatement(ReturnStatementAST *) {} + virtual void visitSimpleDeclaration(SimpleDeclarationAST *) {} + virtual void visitSimpleTypeSpecifier(SimpleTypeSpecifierAST *) {} + virtual void visitSizeofExpression(SizeofExpressionAST *) {} + virtual void visitStringLiteral(StringLiteralAST *) {} + virtual void visitSubscriptExpression(SubscriptExpressionAST *) {} + virtual void visitSwitchStatement(SwitchStatementAST *) {} + virtual void visitTemplateArgument(TemplateArgumentAST *) {} + virtual void visitTemplateDeclaration(TemplateDeclarationAST *) {} + virtual void visitTemplateParameter(TemplateParameterAST *) {} + virtual void visitThrowExpression(ThrowExpressionAST *) {} + virtual void visitTranslationUnit(TranslationUnitAST *) {} + virtual void visitTryBlockStatement(TryBlockStatementAST *) {} + virtual void visitTypeId(TypeIdAST *) {} + virtual void visitTypeIdentification(TypeIdentificationAST *) {} + virtual void visitTypeParameter(TypeParameterAST *) {} + virtual void visitTypedef(TypedefAST *) {} + virtual void visitUnaryExpression(UnaryExpressionAST *) {} + virtual void visitUnqualifiedName(UnqualifiedNameAST *) {} + virtual void visitUsing(UsingAST *) {} + virtual void visitUsingDirective(UsingDirectiveAST *) {} + virtual void visitWhileStatement(WhileStatementAST *) {} + virtual void visitWinDeclSpec(WinDeclSpecAST *) {} + virtual void visitQProperty(QPropertyAST *) {} + virtual void visitForwardDeclarationSpecifier(ForwardDeclarationSpecifierAST *) {} + virtual void visitQEnums(QEnumsAST *) {} + +private: + typedef void (Visitor::*visitor_fun_ptr)(AST *); + static visitor_fun_ptr _S_table[]; +}; + +template +void visitNodes(Visitor *v, const ListNode<_Tp> *nodes) +{ + if (!nodes) + return; + + const ListNode<_Tp> + *it = nodes->toFront(), + *end = it; + + do { + v->visit(it->element); + it = it->next; + } while (it != end); +} + +#endif // VISITOR_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/qtdocparser.cpp b/qtdocparser.cpp new file mode 100644 index 000000000..0651d5fd5 --- /dev/null +++ b/qtdocparser.cpp @@ -0,0 +1,156 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "qtdocparser.h" +#include "reporthandler.h" +#include +#include + +Documentation QtDocParser::retrieveModuleDocumentation() +{ + // TODO: This method of acquiring the module name supposes that the target language uses + // dots as module separators in package names. Improve this. + QString moduleName = QString(packageName()).remove(0, packageName().lastIndexOf('.') + 1); + QString sourceFile = documentationDataDirectory() + '/' + moduleName.toLower() + ".xml"; + + if (!QFile::exists(sourceFile)) { + ReportHandler::warning("Can't find qdoc3 file for module " + + packageName() + ", tried: " + + sourceFile); + return Documentation(); + } + + QXmlQuery xquery; + xquery.setFocus(QUrl(sourceFile)); + + // Module documentation + QString query = "/WebXML/document/page[@name=\"" + moduleName + "\"]/description"; + return Documentation(getDocumentation(xquery, query, DocModificationList())); +} + +void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) +{ + if (!metaClass) + return; + + QString sourceFile = documentationDataDirectory() + '/' + metaClass->name().toLower() + ".xml"; + if (metaClass->enclosingClass()) + sourceFile.replace("::", "-"); + + if (!QFile::exists(sourceFile)) { + ReportHandler::warning("Can't find qdoc3 file for class " + + metaClass->name() + ", tried: " + + sourceFile); + return; + } + + QXmlQuery xquery; + xquery.setFocus(QUrl(sourceFile)); + + QString className = metaClass->name(); + QString nameType = metaClass->enclosingClass() ? "fullname" : "name"; + + // Class/Namespace documentation + QString type = metaClass->isNamespace() ? QLatin1String("namespace") : QLatin1String("class"); + QString query = "/WebXML/document/" + type + "[@" + nameType + "=\"" + className + "\"]/description"; + + DocModificationList signedModifs, classModifs; + foreach (DocModification docModif, metaClass->typeEntry()->docModifications()) { + if (docModif.signature().isEmpty()) + classModifs.append(docModif); + else + signedModifs.append(docModif); + } + + Documentation doc(getDocumentation(xquery, query, classModifs)); + metaClass->setDocumentation(doc); + + + //Functions Documentation + AbstractMetaFunctionList funcs = metaClass->functionsInTargetLang(); + foreach (AbstractMetaFunction *func, funcs) { + if (!func || func->isPrivate()) + continue; + + QString query = "/WebXML/document/" + type + "[@" + nameType + "=\"" + className + "\"]"; + // properties + if (func->isPropertyReader() || func->isPropertyWriter() || func->isPropertyResetter()) { + query += "/property[@name=\"" + func->propertySpec()->name() + "\"]"; + } else { // normal methods + QString isConst = func->isConstant() ? "true" : "false"; + query += "/function[@name=\"" + func->originalName() + + "\" and count(parameter)=" + + QString::number(func->arguments().count()) + + " and @const=\"" + isConst + "\"]"; + + int i = 1; + foreach (AbstractMetaArgument* arg, func->arguments()) { + QString type = arg->type()->name(); + /* XXX Hack to get arguments working inside namespaces types */ + type = type.split("::").last(); + + if (arg->type()->isConstant()) + type.prepend("const "); + + if (arg->type()->isReference()) { + type += " &"; + } if (arg->type()->indirections()) { + type += ' '; + for (int j = 0, max = arg->type()->indirections(); j < max; ++j) + type += '*'; + } + query += "/parameter[" + QString::number(i) + "][@left=\"" + type + "\"]/.."; + ++i; + } + } + query += "/description"; + DocModificationList funcModifs; + foreach (DocModification funcModif, signedModifs) { + if (funcModif.signature() == func->minimalSignature()) + funcModifs.append(funcModif); + } + doc.setValue(getDocumentation(xquery, query, funcModifs)); + func->setDocumentation(doc); + } +#if 0 + // Fields + AbstractMetaFieldList fields = metaClass->fields(); + foreach (AbstractMetaField *field, fields) { + if (field->isPrivate()) + return; + + QString query = "/doxygen/compounddef/sectiondef/memberdef/name[text()=\"" + field->name() + "\"]/.."; + Documentation doc = getDocumentation(DocModificationList(), xquery, query); + field->setDocumentation(doc); + } +#endif + // Enums + AbstractMetaEnumList enums = metaClass->enums(); + foreach (AbstractMetaEnum *meta_enum, enums) { + QString query = "/WebXML/document/" + type + "[@" + nameType + "=\"" + + className + "\"]/enum[@name=\"" + + meta_enum->name() + "\"]/description"; + doc.setValue(getDocumentation(xquery, query, DocModificationList())); + meta_enum->setDocumentation(doc); + } +} diff --git a/qtdocparser.h b/qtdocparser.h new file mode 100644 index 000000000..e4b7089d7 --- /dev/null +++ b/qtdocparser.h @@ -0,0 +1,38 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef QTDOCPARSER_H +#define QTDOCPARSER_H + +#include "docparser.h" + +class QtDocParser : public DocParser +{ +public: + QtDocParser() {} + virtual void fillDocumentation(AbstractMetaClass* metaClass); + virtual Documentation retrieveModuleDocumentation(); +}; + +#endif // QTDOCPARSER_H + diff --git a/reporthandler.cpp b/reporthandler.cpp new file mode 100644 index 000000000..c03100815 --- /dev/null +++ b/reporthandler.cpp @@ -0,0 +1,100 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "reporthandler.h" +#include "typesystem.h" +#include + +#ifndef NOCOLOR +#define COLOR_END "\033[0m" +#define COLOR_WHITE "\033[1;37m" +#define COLOR_YELLOW "\033[1;33m" +#define COLOR_GREEN "\033[0;32m" +#else +#define COLOR_END "" +#define COLOR_WHITE "" +#define COLOR_YELLOW "" +#define COLOR_GREEN "" +#endif + + +bool ReportHandler::m_silent = false; +int ReportHandler::m_warningCount = 0; +int ReportHandler::m_suppressedCount = 0; +QString ReportHandler::m_context; +ReportHandler::DebugLevel ReportHandler::m_debugLevel = NoDebug; +QSet ReportHandler::m_reportedWarnings; +char ReportHandler::m_progressBuffer[1024] = {0}; +ProgressAnimation ReportHandler::m_anim; + + +void ReportHandler::warning(const QString &text) +{ + if (m_silent) + return; + +// Context is useless! +// QString warningText = QString("\r" COLOR_YELLOW "WARNING(%1)" COLOR_END " :: %2").arg(m_context).arg(text); + QString warningText = QString("\033[1K\r" COLOR_YELLOW "WARNING" COLOR_END " :: %2").arg(text); + + TypeDatabase *db = TypeDatabase::instance(); + if (db && db->isSuppressedWarning(text)) { + ++m_suppressedCount; + } else if (!m_reportedWarnings.contains(text)) { + puts(qPrintable(warningText)); + printProgress(); + ++m_warningCount; + + m_reportedWarnings.insert(text); + } +} + +void ReportHandler::progress(const QString& str, ...) +{ + if (m_silent) + return; + QString msg = QString("\033[1K\r" COLOR_WHITE "%1 (%2/%3) " COLOR_END).arg(m_anim.toString()).arg(m_anim.current()).arg(m_anim.max()) + str; + va_list argp; + va_start(argp, str); + vsnprintf(m_progressBuffer, sizeof(m_progressBuffer), msg.toLocal8Bit().constData(), argp); + va_end(argp); + printProgress(); +} + +void ReportHandler::printProgress() +{ + printf(m_progressBuffer); + fflush(stdout); +} + + +void ReportHandler::debug(DebugLevel level, const QString &text) +{ + if (m_debugLevel == NoDebug) + return; + + if (level <= m_debugLevel) { + printf("\r" COLOR_GREEN "DEBUG" COLOR_END " :: %-70s\n", qPrintable(text)); + printProgress(); + } +} diff --git a/reporthandler.h b/reporthandler.h new file mode 100644 index 000000000..e13d3562f --- /dev/null +++ b/reporthandler.h @@ -0,0 +1,154 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef REPORTHANDLER_H +#define REPORTHANDLER_H + +#include +#include +#include + +class ProgressAnimation +{ +public: + ProgressAnimation() + { + anim_data = "|/-\\"; + anim_frame = anim_data; + std::strcpy(anim_string, "[ ]"); + m_current = m_max = 0; + } + const char* toString() + { + step(); + return anim_string; + } + template + void setCollection(T collection) + { + m_current = 1; + m_max = collection.count(); + } + + int current() const + { + return m_current; + } + int max() const + { + return m_max; + } + +private: + const char* anim_data; + char anim_string[4]; + const char* anim_frame; + int m_max; + int m_current; + + void step() + { + if (!*(++anim_frame)) + anim_frame = anim_data; + anim_string[1] = *anim_frame; + m_current++; + } +}; + +class ReportHandler +{ +public: + enum DebugLevel { NoDebug, SparseDebug, MediumDebug, FullDebug }; + + static void setContext(const QString &context) + { + m_context = context; + } + + static DebugLevel debugLevel() + { + return m_debugLevel; + } + static void setDebugLevel(DebugLevel level) + { + m_debugLevel = level; + } + + static int warningCount() + { + return m_warningCount; + } + + static int suppressedCount() + { + return m_suppressedCount; + } + + static void warning(const QString &str); + + template + static void setProgressReference(T collection) + { + m_anim.setCollection(collection); + } + + static void progress(const QString &str, ...); + + static void debugSparse(const QString &str) + { + debug(SparseDebug, str); + } + static void debugMedium(const QString &str) + { + debug(MediumDebug, str); + } + static void debugFull(const QString &str) + { + debug(FullDebug, str); + } + static void debug(DebugLevel level, const QString &str); + + static bool isSilent() + { + return m_silent; + } + static void setSilent(bool silent) + { + m_silent = silent; + } + +private: + static bool m_silent; + static int m_warningCount; + static int m_suppressedCount; + static DebugLevel m_debugLevel; + static QString m_context; + static QSet m_reportedWarnings; + + static ProgressAnimation m_anim; + static char m_progressBuffer[1024]; + + static void printProgress(); +}; + +#endif // REPORTHANDLER_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..8517a28a9 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,6 @@ + +qt4_automoc(testabstractmetaclass.cpp) +add_executable(testabstractmetaclass testabstractmetaclass.cpp) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${apiextractor_SOURCE_DIR}) +target_link_libraries(testabstractmetaclass ${QT_QTTEST_LIBRARY} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} apiextractor) +add_test("AbstractMetaClass" testabstractmetaclass) diff --git a/tests/testabstractmetaclass.cpp b/tests/testabstractmetaclass.cpp new file mode 100644 index 000000000..d7eb77ca2 --- /dev/null +++ b/tests/testabstractmetaclass.cpp @@ -0,0 +1,86 @@ +/* +* This file is part of the API Extractor project. +* +* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +* +* Contact: PySide team +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +* 02110-1301 USA +* +*/ + +#include "testabstractmetaclass.h" +#include "abstractmetabuilder.h" +#include +#include + + +TestAbstractMetaClass::TestAbstractMetaClass() : m_builder(0) +{ +} + +void TestAbstractMetaClass::init() +{ + m_builder = new AbstractMetaBuilder; +} + +void TestAbstractMetaClass::cleanup() +{ + delete m_builder; + m_builder = 0; +} + +void TestAbstractMetaClass::processCode (const char* cppCode, const char* xmlCode ) +{ + QBuffer buffer; + // parse typesystem + buffer.setData(xmlCode); + TypeDatabase::instance()->parseFile(&buffer); + buffer.close(); + // parse C++ code + buffer.setData(cppCode); + m_builder->build(&buffer); +} + +void TestAbstractMetaClass::testClassName() +{ + const char* cppCode ="class ClassName {};"; + const char* xmlCode = ""; + processCode(cppCode, xmlCode); + AbstractMetaClassList classes = m_builder->classes(); + QCOMPARE(classes.count(), 1); + QCOMPARE(classes[0]->name(), QString("ClassName")); +} + +void TestAbstractMetaClass::testClassNameUnderNamespace() +{ + const char* cppCode ="namespace Namespace { class ClassName {}; }"; + const char* xmlCode = "\ + \ + \ + \ + "; + processCode(cppCode, xmlCode); + AbstractMetaClassList classes = m_builder->classes(); + QCOMPARE(classes.count(), 2); // 1 namespace + 1 class + QCOMPARE(classes[0]->name(), QString("ClassName")); + QCOMPARE(classes[0]->qualifiedCppName(), QString("Namespace::ClassName")); + QCOMPARE(classes[1]->name(), QString("Namespace")); +} + + +QTEST_APPLESS_MAIN(TestAbstractMetaClass) + +#include "testabstractmetaclass.moc" diff --git a/tests/testabstractmetaclass.h b/tests/testabstractmetaclass.h new file mode 100644 index 000000000..a15061bbe --- /dev/null +++ b/tests/testabstractmetaclass.h @@ -0,0 +1,47 @@ +/* +* This file is part of the API Extractor project. +* +* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +* +* Contact: PySide team +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +* 02110-1301 USA +* +*/ + +#ifndef TESTABSTRACTMETACLASS_H +#define TESTABSTRACTMETACLASS_H + +#include + +class AbstractMetaBuilder; + +class TestAbstractMetaClass : public QObject +{ + Q_OBJECT +public: + TestAbstractMetaClass(); +private slots: + void init(); + void cleanup(); + void testClassName(); + void testClassNameUnderNamespace(); +private: + AbstractMetaBuilder* m_builder; + + void processCode(const char* cppCode, const char* xmlCode); +}; + +#endif // TESTABSTRACTMETACLASS_H diff --git a/typeparser.cpp b/typeparser.cpp new file mode 100644 index 000000000..7c82a282f --- /dev/null +++ b/typeparser.cpp @@ -0,0 +1,261 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "typeparser.h" + +#include +#include + +class Scanner +{ +public: + enum Token { + StarToken, + AmpersandToken, + LessThanToken, + ColonToken, + CommaToken, + OpenParenToken, + CloseParenToken, + SquareBegin, + SquareEnd, + GreaterThanToken, + + ConstToken, + Identifier, + NoToken + }; + + Scanner(const QString &s) + : m_pos(0), m_length(s.length()), m_chars(s.constData()) + { + } + + Token nextToken(); + QString identifier() const; + +private: + int m_pos; + int m_length; + int m_tokenStart; + const QChar *m_chars; +}; + +QString Scanner::identifier() const +{ + return QString(m_chars + m_tokenStart, m_pos - m_tokenStart); +} + +Scanner::Token Scanner::nextToken() +{ + Token tok = NoToken; + + // remove whitespace + while (m_pos < m_length && m_chars[m_pos] == ' ') + ++m_pos; + + m_tokenStart = m_pos; + + while (m_pos < m_length) { + + const QChar &c = m_chars[m_pos]; + + if (tok == NoToken) { + switch (c.toLatin1()) { + case '*': tok = StarToken; break; + case '&': tok = AmpersandToken; break; + case '<': tok = LessThanToken; break; + case '>': tok = GreaterThanToken; break; + case ',': tok = CommaToken; break; + case '(': tok = OpenParenToken; break; + case ')': tok = CloseParenToken; break; + case '[': tok = SquareBegin; break; + case ']' : tok = SquareEnd; break; + case ':': + tok = ColonToken; + Q_ASSERT(m_pos + 1 < m_length); + ++m_pos; + break; + default: + if (c.isLetterOrNumber() || c == '_') + tok = Identifier; + else + qFatal("Unrecognized character in lexer: %c", c.toLatin1()); + break; + } + } + + if (tok <= GreaterThanToken) { + ++m_pos; + break; + } + + if (tok == Identifier) { + if (c.isLetterOrNumber() || c == '_') + ++m_pos; + else + break; + } + } + + if (tok == Identifier && m_pos - m_tokenStart == 5) { + if (m_chars[m_tokenStart] == 'c' + && m_chars[m_tokenStart + 1] == 'o' + && m_chars[m_tokenStart + 2] == 'n' + && m_chars[m_tokenStart + 3] == 's' + && m_chars[m_tokenStart + 4] == 't') + tok = ConstToken; + } + + return tok; + +} + +TypeParser::Info TypeParser::parse(const QString &str) +{ + Scanner scanner(str); + + Info info; + QStack stack; + stack.push(&info); + + bool colon_prefix = false; + bool in_array = false; + QString array; + + Scanner::Token tok = scanner.nextToken(); + while (tok != Scanner::NoToken) { + +// switch (tok) { +// case Scanner::StarToken: printf(" - *\n"); break; +// case Scanner::AmpersandToken: printf(" - &\n"); break; +// case Scanner::LessThanToken: printf(" - <\n"); break; +// case Scanner::GreaterThanToken: printf(" - >\n"); break; +// case Scanner::ColonToken: printf(" - ::\n"); break; +// case Scanner::CommaToken: printf(" - ,\n"); break; +// case Scanner::ConstToken: printf(" - const\n"); break; +// case Scanner::SquareBegin: printf(" - [\n"); break; +// case Scanner::SquareEnd: printf(" - ]\n"); break; +// case Scanner::Identifier: printf(" - '%s'\n", qPrintable(scanner.identifier())); break; +// default: +// break; +// } + + switch (tok) { + + case Scanner::StarToken: + ++stack.top()->indirections; + break; + + case Scanner::AmpersandToken: + stack.top()->is_reference = true; + break; + + case Scanner::LessThanToken: + stack.top()->template_instantiations << Info(); + stack.push(&stack.top()->template_instantiations.last()); + break; + + case Scanner::CommaToken: + stack.pop(); + stack.top()->template_instantiations << Info(); + stack.push(&stack.top()->template_instantiations.last()); + break; + + case Scanner::GreaterThanToken: + stack.pop(); + break; + + case Scanner::ColonToken: + colon_prefix = true; + break; + + case Scanner::ConstToken: + stack.top()->is_constant = true; + break; + + case Scanner::OpenParenToken: // function pointers not supported + case Scanner::CloseParenToken: { + Info i; + i.is_busted = true; + return i; + } + + + case Scanner::Identifier: + if (in_array) { + array = scanner.identifier(); + } else if (colon_prefix || stack.top()->qualified_name.isEmpty()) { + stack.top()->qualified_name << scanner.identifier(); + colon_prefix = false; + } else { + stack.top()->qualified_name.last().append(" " + scanner.identifier()); + } + break; + + case Scanner::SquareBegin: + in_array = true; + break; + + case Scanner::SquareEnd: + in_array = false; + stack.top()->arrays += array; + break; + + + default: + break; + } + + tok = scanner.nextToken(); + } + + return info; +} + +QString TypeParser::Info::instantiationName() const +{ + QString s(qualified_name.join("::")); + if (!template_instantiations.isEmpty()) { + QStringList insts; + foreach (Info info, template_instantiations) + insts << info.toString(); + s += QString("< %1 >").arg(insts.join(", ")); + } + + return s; +} + +QString TypeParser::Info::toString() const +{ + QString s; + + if (is_constant) s += "const "; + s += instantiationName(); + for (int i = 0; i < arrays.size(); ++i) + s += "[" + arrays.at(i) + "]"; + s += QString(indirections, '*'); + if (is_reference) s += '&'; + + return s; +} diff --git a/typeparser.h b/typeparser.h new file mode 100644 index 000000000..1d0c019db --- /dev/null +++ b/typeparser.h @@ -0,0 +1,52 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef TYPEPARSER_H +#define TYPEPARSER_H + +#include +#include +#include + +class TypeParser +{ +public: + struct Info + { + Info() : is_reference(false), is_constant(false), is_busted(false), indirections(0) { } + QStringList qualified_name; + QStringList arrays; + QList template_instantiations; + uint is_reference : 1; + uint is_constant : 1; + uint is_busted : 1; + uint indirections : 5; + + QString toString() const; + QString instantiationName() const; + }; + + static Info parse(const QString &str); +}; + +#endif // TYPEPARSER_H diff --git a/typesystem.cpp b/typesystem.cpp new file mode 100644 index 000000000..2a17d703b --- /dev/null +++ b/typesystem.cpp @@ -0,0 +1,2085 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "typesystem.h" +#include "generator.h" + +#include + +#include + +QString strings_Object = QLatin1String("Object"); +QString strings_String = QLatin1String("String"); +QString strings_Thread = QLatin1String("Thread"); +QString strings_char = QLatin1String("char"); +QString stringsJavaLang = QLatin1String("java.lang"); +QString strings_jchar = QLatin1String("jchar"); +QString strings_jobject = QLatin1String("jobject"); + +class StackElement +{ +public: + enum ElementType { + None = 0x0, + + // Type tags (0x1, ... , 0xff) + ObjectTypeEntry = 0x1, + ValueTypeEntry = 0x2, + InterfaceTypeEntry = 0x3, + NamespaceTypeEntry = 0x4, + ComplexTypeEntryMask = 0x7, + + // Non-complex type tags (0x8, 0x9, ... , 0xf) + PrimitiveTypeEntry = 0x8, + EnumTypeEntry = 0x9, + ContainerTypeEntry = 0xa, + TypeEntryMask = 0xf, + + // Documentation tags + InjectDocumentation = 0x10, + ModifyDocumentation = 0x20, + DocumentationMask = 0xf0, + + // Simple tags (0x100, 0x200, ... , 0xf00) + ExtraIncludes = 0x100, + Include = 0x200, + ModifyFunction = 0x300, + ModifyField = 0x400, + Root = 0x500, + CustomMetaConstructor = 0x600, + CustomMetaDestructor = 0x700, + ArgumentMap = 0x800, + SuppressedWarning = 0x900, + Rejection = 0xa00, + LoadTypesystem = 0xb00, + RejectEnumValue = 0xc00, + Template = 0xd00, + TemplateInstanceEnum = 0xe00, + Replace = 0xf00, + SimpleMask = 0xf00, + + // Code snip tags (0x1000, 0x2000, ... , 0xf000) + InjectCode = 0x1000, + InjectCodeInFunction = 0x2000, + CodeSnipMask = 0xf000, + + // Function modifier tags (0x010000, 0x020000, ... , 0xf00000) + Access = 0x010000, + Removal = 0x020000, + Rename = 0x040000, + ModifyArgument = 0x080000, + Thread = 0x100000, + FunctionModifiers = 0xff0000, + + // Argument modifier tags (0x01000000 ... 0xf0000000) + ConversionRule = 0x01000000, + ReplaceType = 0x02000000, + ReplaceDefaultExpression = 0x04000000, + RemoveArgument = 0x08000000, + DefineOwnership = 0x10000000, + RemoveDefaultExpression = 0x20000000, + NoNullPointers = 0x40000000, +#if 0 + ReferenceCount = 0x80000000, +#endif + ParentOwner = 0x80000000, + ArgumentModifiers = 0xff000000 + }; + + StackElement(StackElement *p) : entry(0), type(None), parent(p) { } + + TypeEntry *entry; + ElementType type; + StackElement *parent; + + union { + TemplateInstance *templateInstance; + TemplateEntry *templateEntry; + CustomFunction *customFunction; + } value; +}; + +class Handler : public QXmlDefaultHandler +{ +public: + Handler(TypeDatabase *database, bool generate) + : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) + { + m_currentEnum = 0; + current = 0; + + tagNames["rejection"] = StackElement::Rejection; + tagNames["primitive-type"] = StackElement::PrimitiveTypeEntry; + tagNames["container-type"] = StackElement::ContainerTypeEntry; + tagNames["object-type"] = StackElement::ObjectTypeEntry; + tagNames["value-type"] = StackElement::ValueTypeEntry; + tagNames["interface-type"] = StackElement::InterfaceTypeEntry; + tagNames["namespace-type"] = StackElement::NamespaceTypeEntry; + tagNames["enum-type"] = StackElement::EnumTypeEntry; + tagNames["extra-includes"] = StackElement::ExtraIncludes; + tagNames["include"] = StackElement::Include; + tagNames["inject-code"] = StackElement::InjectCode; + tagNames["modify-function"] = StackElement::ModifyFunction; + tagNames["modify-field"] = StackElement::ModifyField; + tagNames["access"] = StackElement::Access; + tagNames["remove"] = StackElement::Removal; + tagNames["rename"] = StackElement::Rename; + tagNames["typesystem"] = StackElement::Root; + tagNames["custom-constructor"] = StackElement::CustomMetaConstructor; + tagNames["custom-destructor"] = StackElement::CustomMetaDestructor; + tagNames["argument-map"] = StackElement::ArgumentMap; + tagNames["suppress-warning"] = StackElement::SuppressedWarning; + tagNames["load-typesystem"] = StackElement::LoadTypesystem; + tagNames["define-ownership"] = StackElement::DefineOwnership; + tagNames["replace-default-expression"] = StackElement::ReplaceDefaultExpression; + tagNames["reject-enum-value"] = StackElement::RejectEnumValue; + tagNames["replace-type"] = StackElement::ReplaceType; + tagNames["conversion-rule"] = StackElement::ConversionRule; + tagNames["modify-argument"] = StackElement::ModifyArgument; + tagNames["remove-argument"] = StackElement::RemoveArgument; + tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression; + tagNames["template"] = StackElement::Template; + tagNames["insert-template"] = StackElement::TemplateInstanceEnum; + tagNames["replace"] = StackElement::Replace; + tagNames["no-null-pointer"] = StackElement::NoNullPointers; +#if 0 + tagNames["reference-count"] = StackElement::ReferenceCount; +#endif + tagNames["parent"] = StackElement::ParentOwner; + tagNames["inject-documentation"] = StackElement::InjectDocumentation; + tagNames["modify-documentation"] = StackElement::ModifyDocumentation; + } + + bool startElement(const QString &namespaceURI, const QString &localName, + const QString &qName, const QXmlAttributes &atts); + bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName); + + QString errorString() const + { + return m_error; + } + bool error(const QXmlParseException &exception); + bool fatalError(const QXmlParseException &exception); + bool warning(const QXmlParseException &exception); + + bool characters(const QString &ch); + +private: + void fetchAttributeValues(const QString &name, const QXmlAttributes &atts, + QHash *acceptedAttributes); + + bool importFileElement(const QXmlAttributes &atts); + bool convertBoolean(const QString &, const QString &, bool); + + TypeDatabase *m_database; + StackElement* current; + QString m_defaultPackage; + QString m_defaultSuperclass; + QString m_error; + TypeEntry::CodeGeneration m_generate; + + EnumTypeEntry *m_currentEnum; + + CodeSnipList m_codeSnips; + FunctionModificationList m_functionMods; + FieldModificationList m_fieldMods; + DocModificationList m_docModifications; + + QHash tagNames; + QString m_currentSignature; +}; + +bool Handler::error(const QXmlParseException &e) +{ + qWarning("Error: line=%d, column=%d, message=%s\n", + e.lineNumber(), e.columnNumber(), qPrintable(e.message())); + return false; +} + +bool Handler::fatalError(const QXmlParseException &e) +{ + qWarning("Fatal error: line=%d, column=%d, message=%s\n", + e.lineNumber(), e.columnNumber(), qPrintable(e.message())); + + return false; +} + +bool Handler::warning(const QXmlParseException &e) +{ + qWarning("Warning: line=%d, column=%d, message=%s\n", + e.lineNumber(), e.columnNumber(), qPrintable(e.message())); + + return false; +} + +void Handler::fetchAttributeValues(const QString &name, const QXmlAttributes &atts, + QHash *acceptedAttributes) +{ + Q_ASSERT(acceptedAttributes); + + for (int i = 0; i < atts.length(); ++i) { + QString key = atts.localName(i).toLower(); + QString val = atts.value(i); + + if (!acceptedAttributes->contains(key)) + ReportHandler::warning(QString("Unknown attribute for '%1': '%2'").arg(name).arg(key)); + else + (*acceptedAttributes)[key] = val; + + } +} + +bool Handler::endElement(const QString &, const QString &localName, const QString &) +{ + QString tagName = localName.toLower(); + if (tagName == "import-file") + return true; + + if (!current) + return true; + + switch (current->type) { + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::InterfaceTypeEntry: + case StackElement::NamespaceTypeEntry: { + ComplexTypeEntry *centry = static_cast(current->entry); + centry->setFunctionModifications(m_functionMods); + centry->setFieldModifications(m_fieldMods); + centry->setCodeSnips(m_codeSnips); + centry->setDocModification(m_docModifications); + + if (centry->designatedInterface()) { + centry->designatedInterface()->setCodeSnips(m_codeSnips); + centry->designatedInterface()->setFunctionModifications(m_functionMods); + } + m_codeSnips = CodeSnipList(); + m_functionMods = FunctionModificationList(); + m_fieldMods = FieldModificationList(); + m_docModifications = DocModificationList(); + } + break; + case StackElement::CustomMetaConstructor: { + current->entry->setCustomConstructor(*current->value.customFunction); + delete current->value.customFunction; + } + break; + case StackElement::CustomMetaDestructor: { + current->entry->setCustomDestructor(*current->value.customFunction); + delete current->value.customFunction; + } + break; + case StackElement::EnumTypeEntry: + current->entry->setDocModification(m_docModifications); + m_docModifications = DocModificationList(); + m_currentEnum = 0; + break; + case StackElement::Template: + m_database->addTemplate(current->value.templateEntry); + break; + case StackElement::TemplateInstanceEnum: + if (current->parent->type == StackElement::InjectCode) + m_codeSnips.last().addTemplateInstance(current->value.templateInstance); + else if (current->parent->type == StackElement::Template) + current->parent->value.templateEntry->addTemplateInstance(current->value.templateInstance); + else if (current->parent->type == StackElement::CustomMetaConstructor + || current->parent->type == StackElement::CustomMetaConstructor) + current->parent->value.customFunction->addTemplateInstance(current->value.templateInstance); + else if (current->parent->type == StackElement::ConversionRule) + m_functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(current->value.templateInstance); + else if (current->parent->type == StackElement::InjectCodeInFunction) + m_functionMods.last().snips.last().addTemplateInstance(current->value.templateInstance); + + break; + default: + break; + } + + StackElement *child = current; + current = current->parent; + delete(child); + + return true; +} + +bool Handler::characters(const QString &ch) +{ + if (current->type == StackElement::Template) { + current->value.templateEntry->addCode(ch); + return true; + } + + if (current->type == StackElement::CustomMetaConstructor || current->type == StackElement::CustomMetaDestructor) { + current->value.customFunction->addCode(ch); + return true; + } + + if (current->type == StackElement::ConversionRule + && current->parent->type == StackElement::ModifyArgument) { + m_functionMods.last().argument_mods.last().conversion_rules.last().addCode(ch); + return true; + } + + if (current->parent) { + if ((current->type & StackElement::CodeSnipMask)) { + switch (current->parent->type) { + case StackElement::Root: + ((TypeSystemTypeEntry *) current->parent->entry)->codeSnips().last().addCode(ch); + break; + case StackElement::ModifyFunction: + m_functionMods.last().snips.last().addCode(ch); + m_functionMods.last().modifiers |= FunctionModification::CodeInjection; + break; + case StackElement::NamespaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::InterfaceTypeEntry: + m_codeSnips.last().addCode(ch); + break; + default: + Q_ASSERT(false); + }; + return true; + } + } + + if (current->type & StackElement::DocumentationMask) + m_docModifications.last().setCode(ch); + + return true; +} + +bool Handler::importFileElement(const QXmlAttributes &atts) +{ + QString fileName = atts.value("name"); + if (fileName.isEmpty()) { + m_error = "Required attribute 'name' missing for include-file tag."; + return false; + } + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + file.setFileName(":/trolltech/generator/" + fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_error = QString("Could not open file: '%1'").arg(fileName); + return false; + } + } + + QString quoteFrom = atts.value("quote-after-line"); + bool foundFromOk = quoteFrom.isEmpty(); + bool from = quoteFrom.isEmpty(); + + QString quoteTo = atts.value("quote-before-line"); + bool foundToOk = quoteTo.isEmpty(); + bool to = true; + + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine(); + if (from && to && line.contains(quoteTo)) { + to = false; + foundToOk = true; + break; + } + if (from && to) + characters(line + "\n"); + if (!from && line.contains(quoteFrom)) { + from = true; + foundFromOk = true; + } + } + if (!foundFromOk || !foundToOk) { + QString fromError = QString("Could not find quote-after-line='%1' in file '%2'.").arg(quoteFrom).arg(fileName); + QString toError = QString("Could not find quote-before-line='%1' in file '%2'.").arg(quoteTo).arg(fileName); + + if (!foundToOk) + m_error = toError; + if (!foundFromOk) + m_error = fromError; + if (!foundFromOk && !foundToOk) + m_error = fromError + " " + toError; + return false; + } + + return true; +} + +bool Handler::convertBoolean(const QString &_value, const QString &attributeName, bool defaultValue) +{ + QString value = _value.toLower(); + if (value == "true" || value == "yes") + return true; + else if (value == "false" || value == "no") + return false; + else { + QString warn = QString("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.") + .arg(value).arg(attributeName).arg(defaultValue ? "yes" : "no"); + + ReportHandler::warning(warn); + return defaultValue; + } +} + +bool Handler::startElement(const QString &, const QString &n, + const QString &, const QXmlAttributes &atts) +{ + QString tagName = n.toLower(); + if (tagName == "import-file") + return importFileElement(atts); + + + StackElement *element = new StackElement(current); + + if (!tagNames.contains(tagName)) { + m_error = QString("Unknown tag name: '%1'").arg(tagName); + return false; + } + + element->type = tagNames[tagName]; + if (element->type & StackElement::TypeEntryMask) { + if (current->type != StackElement::Root) { + m_error = "Nested types not supported"; + return false; + } + + QHash attributes; + attributes["name"] = QString(); + + switch (element->type) { + case StackElement::PrimitiveTypeEntry: + attributes["target-lang-name"] = QString(); + attributes["target-lang-api-name"] = QString(); + attributes["preferred-conversion"] = "yes"; + attributes["preferred-target-lang-type"] = "yes"; + break; + case StackElement::ContainerTypeEntry: + attributes["type"] = QString(); + break; + case StackElement::EnumTypeEntry: + attributes["flags"] = "no"; + attributes["upper-bound"] = QString(); + attributes["lower-bound"] = QString(); + attributes["force-integer"] = "no"; + attributes["extensible"] = "no"; + + break; + + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + attributes["force-abstract"] = QString("no"); + attributes["deprecated"] = QString("no"); + attributes["hash-function"] = QString(""); + // fall throooough + case StackElement::InterfaceTypeEntry: + attributes["default-superclass"] = m_defaultSuperclass; + attributes["polymorphic-id-expression"] = QString(); + attributes["delete-in-main-thread"] = QString("no"); + attributes["held-type"] = QString(); + attributes["copyable"] = QString(); + // fall through + case StackElement::NamespaceTypeEntry: + attributes["target-lang-name"] = QString(); + attributes["package"] = m_defaultPackage; + attributes["expense-cost"] = "1"; + attributes["expense-limit"] = "none"; + attributes["polymorphic-base"] = QString("no"); + attributes["generate"] = QString("yes"); + attributes["target-type"] = QString(); + attributes["generic-class"] = QString("no"); + break; + default: + { } // nada + }; + + fetchAttributeValues(tagName, atts, &attributes); + + QString name = attributes["name"]; + + // We need to be able to have duplicate primitive type entries, + // or it's not possible to cover all primitive target language + // types (which we need to do in order to support fake meta objects) + if (element->type != StackElement::PrimitiveTypeEntry) { + TypeEntry *tmp = m_database->findType(name); + if (tmp) + ReportHandler::warning(QString("Duplicate type entry: '%1'").arg(name)); + } + + if (name.isEmpty()) { + m_error = "no 'name' attribute specified"; + return false; + } + switch (element->type) { + case StackElement::PrimitiveTypeEntry: { + QString targetLangName = attributes["target-lang-name"]; + QString targetLangApiName = attributes["target-lang-api-name"]; + QString preferredConversion = attributes["preferred-conversion"].toLower(); + QString preferredTargetLangType = attributes["preferred-target-lang-type"].toLower(); + + if (targetLangName.isEmpty()) + targetLangName = name; + if (targetLangApiName.isEmpty()) + targetLangApiName = name; + + PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name); + type->setCodeGeneration(m_generate); + type->setTargetLangName(targetLangName); + type->setTargetLangApiName(targetLangApiName); + + bool preferred; + preferred = convertBoolean(preferredConversion, "preferred-conversion", true); + type->setPreferredConversion(preferred); + preferred = convertBoolean(preferredTargetLangType, + "preferred-target-lang-type", true); + type->setPreferredTargetLangType(preferred); + + element->entry = type; + } + break; + case StackElement::ContainerTypeEntry: + { + QString typeName = attributes["type"]; + ContainerTypeEntry::Type containerType = + ContainerTypeEntry::containerTypeFromString(typeName); + if (typeName.isEmpty()) { + m_error = "no 'type' attribute specified"; + return false; + } else if (containerType == ContainerTypeEntry::NoContainer) { + m_error = "there is no container of type " + containerType; + return false; + } + + ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType); + type->setCodeGeneration(m_generate); + element->entry = type; + } + break; + case StackElement::EnumTypeEntry: { + QStringList names = name.split(QLatin1String("::")); + + if (names.size() == 1) + m_currentEnum = new EnumTypeEntry(QString(), name); + else + m_currentEnum = + new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"), + names.last()); + element->entry = m_currentEnum; + m_currentEnum->setCodeGeneration(m_generate); + m_currentEnum->setTargetLangPackage(m_defaultPackage); + m_currentEnum->setUpperBound(attributes["upper-bound"]); + m_currentEnum->setLowerBound(attributes["lower-bound"]); + m_currentEnum->setForceInteger(convertBoolean(attributes["force-integer"], "force-integer", false)); + m_currentEnum->setExtensible(convertBoolean(attributes["extensible"], "extensible", false)); + + // put in the flags parallel... + if (!attributes["flags"].isEmpty() && attributes["flags"].toLower() != "no") { + FlagsTypeEntry *ftype = new FlagsTypeEntry("QFlags<" + name + ">"); + ftype->setOriginator(m_currentEnum); + ftype->setOriginalName(attributes["flags"]); + ftype->setCodeGeneration(m_generate); + QString n = ftype->originalName(); + + QStringList lst = n.split("::"); + if (QStringList(lst.mid(0, lst.size() - 1)).join("::") != m_currentEnum->targetLangQualifier()) { + ReportHandler::warning(QString("enum %1 and flags %2 differ in qualifiers") + .arg(m_currentEnum->targetLangQualifier()) + .arg(lst.at(0))); + } + + ftype->setFlagsName(lst.last()); + m_currentEnum->setFlags(ftype); + + m_database->addFlagsType(ftype); + m_database->addType(ftype); + } + } + break; + + case StackElement::InterfaceTypeEntry: { + ObjectTypeEntry *otype = new ObjectTypeEntry(name); + QString targetLangName = attributes["target-lang-name"]; + if (targetLangName.isEmpty()) + targetLangName = name; + InterfaceTypeEntry *itype = + new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName)); + + if (!convertBoolean(attributes["generate"], "generate", true)) + itype->setCodeGeneration(TypeEntry::GenerateForSubclass); + else + itype->setCodeGeneration(m_generate); + otype->setDesignatedInterface(itype); + itype->setOrigin(otype); + element->entry = otype; + } + // fall through + case StackElement::NamespaceTypeEntry: + if (!element->entry) + element->entry = new NamespaceTypeEntry(name); + + // fall through + case StackElement::ObjectTypeEntry: + if (!element->entry) + element->entry = new ObjectTypeEntry(name); + + // fall through + case StackElement::ValueTypeEntry: { + if (!element->entry) + element->entry = new ValueTypeEntry(name); + + + ComplexTypeEntry *ctype = static_cast(element->entry); + ctype->setTargetLangPackage(attributes["package"]); + ctype->setDefaultSuperclass(attributes["default-superclass"]); + ctype->setGenericClass(convertBoolean(attributes["generic-class"], "generic-class", false)); + + if (!convertBoolean(attributes["generate"], "generate", true)) + element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass); + else + element->entry->setCodeGeneration(m_generate); + + QString targetLangName = attributes["target-lang-name"]; + if (!targetLangName.isEmpty()) + ctype->setTargetLangName(targetLangName); + + // The expense policy + QString limit = attributes["expense-limit"]; + if (!limit.isEmpty() && limit != "none") { + ExpensePolicy ep; + ep.limit = limit.toInt(); + ep.cost = attributes["expense-cost"]; + ctype->setExpensePolicy(ep); + } + + + ctype->setIsPolymorphicBase(convertBoolean(attributes["polymorphic-base"], "polymorphic-base", false)); + ctype->setPolymorphicIdValue(attributes["polymorphic-id-expression"]); + //Copyable + if (attributes["copyable"].isEmpty()) + ctype->setCopyable(ComplexTypeEntry::Unknown); + else { + if (convertBoolean(attributes["copyable"], "copyable", false)) + ctype->setCopyable(ComplexTypeEntry::CopyableSet); + else + ctype->setCopyable(ComplexTypeEntry::NonCopyableSet); + + } + + if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) + ctype->setHashFunction(attributes["hash-function"]); + + + ctype->setHeldType(attributes["held-type"]); + + if (element->type == StackElement::ObjectTypeEntry + || element->type == StackElement::ValueTypeEntry) { + if (convertBoolean(attributes["force-abstract"], "force-abstract", false)) + ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract); + if (convertBoolean(attributes["deprecated"], "deprecated", false)) + ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); + } + + if (element->type == StackElement::InterfaceTypeEntry + || element->type == StackElement::ValueTypeEntry + || element->type == StackElement::ObjectTypeEntry) { + if (convertBoolean(attributes["delete-in-main-thread"], "delete-in-main-thread", false)) + ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread); + } + + QString targetType = attributes["target-type"]; + if (!targetType.isEmpty() && element->entry->isComplex()) + static_cast(element->entry)->setTargetType(targetType); + + // ctype->setInclude(Include(Include::IncludePath, ctype->name())); + ctype = ctype->designatedInterface(); + if (ctype) + ctype->setTargetLangPackage(attributes["package"]); + + } + break; + default: + Q_ASSERT(false); + }; + + if (element->entry) + m_database->addType(element->entry); + else + ReportHandler::warning(QString("Type: %1 was rejected by typesystem").arg(name)); + + } else if (element->type == StackElement::InjectDocumentation) { + // check the XML tag attributes + QHash attributes; + attributes["mode"] = "replace"; + attributes["format"] = "native"; + + fetchAttributeValues(tagName, atts, &attributes); + + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (current->parent && current->parent->type & validParent) { + QString modeName = attributes["mode"]; + DocModification::Mode mode; + if (modeName == "append") { + mode = DocModification::Append; + } else if (modeName == "prepend") { + mode = DocModification::Prepend; + } else if (modeName == "replace") { + mode = DocModification::Replace; + } else { + m_error = "Unknow documentation injection mode: " + modeName; + return false; + } + + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangCode; + languageNames["native"] = TypeSystem::NativeCode; + } + + QString format = attributes["format"].toLower(); + TypeSystem::Language lang = languageNames.value(format, TypeSystem::NoLanguage); + if (lang == TypeSystem::NoLanguage) { + m_error = QString("unsupported class attribute: '%1'").arg(format); + return false; + } + + QString signature = current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature; + DocModification mod(mode, signature); + mod.format = lang; + m_docModifications << mod; + } else { + m_error = "inject-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"; + return false; + } + } else if (element->type == StackElement::ModifyDocumentation) { + // check the XML tag attributes + QHash attributes; + attributes["xpath"] = QString(); + fetchAttributeValues(tagName, atts, &attributes); + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (current->parent && current->parent->type & validParent) + m_docModifications << DocModification(attributes["xpath"], m_currentSignature); + else { + m_error = "modify-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"; + return false; + } + } else if (element->type != StackElement::None) { + bool topLevel = element->type == StackElement::Root + || element->type == StackElement::SuppressedWarning + || element->type == StackElement::Rejection + || element->type == StackElement::LoadTypesystem + || element->type == StackElement::InjectCode + || element->type == StackElement::ConversionRule + || element->type == StackElement::Template; + + if (!topLevel && current->type == StackElement::Root) { + m_error = QString("Tag requires parent: '%1'").arg(tagName); + return false; + } + + StackElement topElement = !current ? StackElement(0) : *current; + element->entry = topElement.entry; + + QHash attributes; + switch (element->type) { + case StackElement::Root: + attributes["package"] = QString(); + attributes["default-superclass"] = QString(); + break; + case StackElement::LoadTypesystem: + attributes["name"] = QString(); + attributes["generate"] = "yes"; + break; + case StackElement::NoNullPointers: + attributes["default-value"] = QString(); + break; + case StackElement::SuppressedWarning: + attributes["text"] = QString(); + break; + case StackElement::ReplaceDefaultExpression: + attributes["with"] = QString(); + break; + case StackElement::DefineOwnership: + attributes["class"] = "target"; + attributes["owner"] = ""; + break; + case StackElement::ModifyFunction: + attributes["signature"] = QString(); + attributes["access"] = QString(); + attributes["remove"] = QString(); + attributes["rename"] = QString(); + attributes["deprecated"] = QString("no"); + attributes["associated-to"] = QString(); + attributes["virtual-slot"] = QString("no"); + attributes["thread"] = QString("no"); + attributes["allow-thread"] = QString("no"); + break; + case StackElement::ModifyArgument: + attributes["index"] = QString(); + attributes["replace-value"] = QString(); + attributes["invalidate-after-use"] = QString("no"); + break; + case StackElement::ModifyField: + attributes["name"] = QString(); + attributes["write"] = "true"; + attributes["read"] = "true"; + break; + case StackElement::Access: + attributes["modifier"] = QString(); + break; + case StackElement::Include: + attributes["file-name"] = QString(); + attributes["location"] = QString(); + break; + case StackElement::CustomMetaConstructor: + attributes["name"] = topElement.entry->name().toLower() + "_create"; + attributes["param-name"] = "copy"; + break; + case StackElement::CustomMetaDestructor: + attributes["name"] = topElement.entry->name().toLower() + "_delete"; + attributes["param-name"] = "copy"; + break; + case StackElement::ReplaceType: + attributes["modified-type"] = QString(); + break; + case StackElement::InjectCode: + attributes["class"] = "target"; + attributes["position"] = "beginning"; + attributes["file"] = QString(); + break; + case StackElement::ConversionRule: + attributes["class"] = ""; + attributes["file"] = ""; + break; + case StackElement::RejectEnumValue: + attributes["name"] = ""; + break; + case StackElement::ArgumentMap: + attributes["index"] = "1"; + attributes["meta-name"] = QString(); + break; + case StackElement::Rename: + attributes["to"] = QString(); + break; + case StackElement::Rejection: + attributes["class"] = "*"; + attributes["function-name"] = "*"; + attributes["field-name"] = "*"; + attributes["enum-name"] = "*"; + break; + case StackElement::Removal: + attributes["class"] = "all"; + break; + case StackElement::Template: + attributes["name"] = QString(); + break; + case StackElement::TemplateInstanceEnum: + attributes["name"] = QString(); + break; + case StackElement::Replace: + attributes["from"] = QString(); + attributes["to"] = QString(); + break; +#if 0 + case StackElement::ReferenceCount: + attributes["action"] = QString(); + break; +#endif + case StackElement::ParentOwner: + attributes["index"] = QString(); + attributes["action"] = QString(); + default: + { } // nada + }; + + if (attributes.count() > 0) + fetchAttributeValues(tagName, atts, &attributes); + + switch (element->type) { + case StackElement::Root: + m_defaultPackage = attributes["package"]; + m_defaultSuperclass = attributes["default-superclass"]; + element->type = StackElement::Root; + element->entry = new TypeSystemTypeEntry(m_defaultPackage); + + if ((m_generate == TypeEntry::GenerateForSubclass || + m_generate == TypeEntry::GenerateNothing) && m_defaultPackage != "") + TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); + + + if (!element->entry->qualifiedCppName().isEmpty()) + m_database->addType(element->entry); + break; + case StackElement::LoadTypesystem: { + QString name = attributes["name"]; + if (name.isEmpty()) { + m_error = "No typesystem name specified"; + return false; + } + + if (!m_database->parseFile(name, convertBoolean(attributes["generate"], "generate", true))) { + m_error = QString("Failed to parse: '%1'").arg(name); + return false; + } + } + break; + case StackElement::RejectEnumValue: { + if (!m_currentEnum) { + m_error = " node must be used inside a node"; + return false; + } + QString name = attributes["name"]; + + bool added = false; + if (!name.isEmpty()) { + added = true; + m_currentEnum->addEnumValueRejection(name); + } + + } break; + case StackElement::ReplaceType: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "Type replacement can only be specified for argument modifications"; + return false; + } + + if (attributes["modified-type"].isEmpty()) { + m_error = "Type replacement requires 'modified-type' attribute"; + return false; + } + + m_functionMods.last().argument_mods.last().modified_type = attributes["modified-type"]; + } + break; + case StackElement::ConversionRule: { + if (topElement.type != StackElement::ModifyArgument + && topElement.type != StackElement::PrimitiveTypeEntry + && topElement.type != StackElement::ContainerTypeEntry) { + m_error = "Conversion rules can only be specified for argument modification" + " and to primitive or container types conversion."; + return false; + } + + if (topElement.type == StackElement::ModifyArgument) { + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangCode; + languageNames["native"] = TypeSystem::NativeCode; + } + + QString languageAttribute = attributes["class"].toLower(); + TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage); + if (lang == TypeSystem::NoLanguage) { + m_error = QString("unsupported class attribute: '%1'").arg(lang); + return false; + } + + CodeSnip snip; + snip.language = lang; + m_functionMods.last().argument_mods.last().conversion_rules.append(snip); + } else { + QString sourceFile = attributes["file"].toLower(); + if (sourceFile.isEmpty()) { + m_error = QString("'file' attribute required; the source file containing the" + " containing the conversion functions must be provided"); + return false; + } + + //Handler constructor.... + if (m_generate != TypeEntry::GenerateForSubclass + && m_generate != TypeEntry::GenerateNothing) { + if (QFile::exists(sourceFile)) { + QFile conversionSource(sourceFile); + if (conversionSource.open(QIODevice::ReadOnly)) { + CodeSnip snip; + snip.addCode(conversionSource.readAll()); + topElement.entry->addCodeSnip(snip); + } + } else { + ReportHandler::warning("File containing conversion code for " + + topElement.entry->name() + + " type does not exist: " + + sourceFile); + } + } + + } + } + + break; + case StackElement::ModifyArgument: { + if (topElement.type != StackElement::ModifyFunction) { + m_error = QString::fromLatin1("argument modification requires function" + " modification as parent, was %1") + .arg(topElement.type, 0, 16); + return false; + } + + QString index = attributes["index"]; + if (index == "return") + index = "0"; + else if (index == "this") + index = "-1"; + + bool ok = false; + int idx = index.toInt(&ok); + if (!ok) { + m_error = QString("Cannot convert '%1' to integer").arg(index); + return false; + } + + QString replace_value = attributes["replace-value"]; + + if (!replace_value.isEmpty() && idx) { + m_error = QString("replace-value is only supported for return values (index=0)."); + return false; + } + + ArgumentModification argumentModification = ArgumentModification(idx); + argumentModification.replace_value = replace_value; + argumentModification.resetAfterUse = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false); + m_functionMods.last().argument_mods.append(argumentModification); + } + break; + case StackElement::NoNullPointers: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "no-null-pointer requires argument modification as parent"; + return false; + } + + m_functionMods.last().argument_mods.last().noNullPointers = true; + if (!m_functionMods.last().argument_mods.last().index) + m_functionMods.last().argument_mods.last().nullPointerDefaultValue = attributes["default-value"]; + else if (!attributes["default-value"].isEmpty()) + ReportHandler::warning("default values for null pointer guards are only effective for return values"); + + } + break; + case StackElement::DefineOwnership: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "define-ownership requires argument modification as parent"; + return false; + } + + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangCode; + languageNames["shell"] = TypeSystem::ShellCode; + } + + QString classAttribute = attributes["class"].toLower(); + TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage); + if (lang == TypeSystem::NoLanguage) { + m_error = QString("unsupported class attribute: '%1'").arg(classAttribute); + return false; + } + + static QHash ownershipNames; + if (ownershipNames.isEmpty()) { + ownershipNames["target"] = TypeSystem::TargetLangOwnership; + ownershipNames["c++"] = TypeSystem::CppOwnership; + ownershipNames["default"] = TypeSystem::DefaultOwnership; + } + + QString ownershipAttribute = attributes["owner"].toLower(); + TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership); + if (owner == TypeSystem::InvalidOwnership) { + m_error = QString("unsupported owner attribute: '%1'").arg(ownershipAttribute); + return false; + } + + m_functionMods.last().argument_mods.last().ownerships[lang] = owner; + } + break; + case StackElement::SuppressedWarning: + if (attributes["text"].isEmpty()) + ReportHandler::warning("Suppressed warning with no text specified"); + else + m_database->addSuppressedWarning(attributes["text"]); + break; + case StackElement::ArgumentMap: { + if (!(topElement.type & StackElement::CodeSnipMask)) { + m_error = "Argument maps requires code injection as parent"; + return false; + } + + bool ok; + int pos = attributes["index"].toInt(&ok); + if (!ok) { + m_error = QString("Can't convert position '%1' to integer") + .arg(attributes["position"]); + return false; + } + + if (pos <= 0) { + m_error = QString("Argument position %1 must be a positive number").arg(pos); + return false; + } + + QString meta_name = attributes["meta-name"]; + if (meta_name.isEmpty()) + ReportHandler::warning("Empty meta name in argument map"); + + + if (topElement.type == StackElement::InjectCodeInFunction) + m_functionMods.last().snips.last().argumentMap[pos] = meta_name; + else { + ReportHandler::warning("Argument maps are only useful for injection of code " + "into functions."); + } + } + break; + case StackElement::Removal: { + if (topElement.type != StackElement::ModifyFunction) { + m_error = "Function modification parent required"; + return false; + } + + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangAndNativeCode; + languageNames["all"] = TypeSystem::All; + } + + QString languageAttribute = attributes["class"].toLower(); + TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage); + if (lang == TypeSystem::NoLanguage) { + m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute); + return false; + } + + m_functionMods.last().removal = lang; + } + break; + case StackElement::Rename: + case StackElement::Access: { + if (topElement.type != StackElement::ModifyField + && topElement.type != StackElement::ModifyFunction) { + m_error = "Function or field modification parent required"; + return false; + } + + Modification *mod = 0; + if (topElement.type == StackElement::ModifyFunction) + mod = &m_functionMods.last(); + else + mod = &m_fieldMods.last(); + + QString modifier; + if (element->type == StackElement::Rename) { + modifier = "rename"; + QString renamed_to = attributes["to"]; + if (renamed_to.isEmpty()) { + m_error = "Rename modifier requires 'to' attribute"; + return false; + } + + if (topElement.type == StackElement::ModifyFunction) + mod->setRenamedTo(renamed_to); + else + mod->setRenamedTo(renamed_to); + } else + modifier = attributes["modifier"].toLower(); + + + if (modifier.isEmpty()) { + m_error = "No access modification specified"; + return false; + } + + static QHash modifierNames; + if (modifierNames.isEmpty()) { + modifierNames["private"] = Modification::Private; + modifierNames["public"] = Modification::Public; + modifierNames["protected"] = Modification::Protected; + modifierNames["friendly"] = Modification::Friendly; + modifierNames["rename"] = Modification::Rename; + modifierNames["final"] = Modification::Final; + modifierNames["non-final"] = Modification::NonFinal; + } + + if (!modifierNames.contains(modifier)) { + m_error = QString("Unknown access modifier: '%1'").arg(modifier); + return false; + } + + mod->modifiers |= modifierNames[modifier]; + } + break; + case StackElement::RemoveArgument: + if (topElement.type != StackElement::ModifyArgument) { + m_error = "Removing argument requires argument modification as parent"; + return false; + } + + m_functionMods.last().argument_mods.last().removed = true; + + break; + + case StackElement::ModifyField: { + QString name = attributes["name"]; + if (name.isEmpty()) + break; + FieldModification fm; + fm.name = name; + fm.modifiers = 0; + + QString read = attributes["read"]; + QString write = attributes["write"]; + + if (read == "true") fm.modifiers |= FieldModification::Readable; + if (write == "true") fm.modifiers |= FieldModification::Writable; + + m_fieldMods << fm; + } + break; + case StackElement::ModifyFunction: { + if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { + m_error = QString::fromLatin1("Modify function requires complex type as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + QString signature = attributes["signature"]; + + signature = QMetaObject::normalizedSignature(signature.toLocal8Bit().constData()); + if (signature.isEmpty()) { + m_error = "No signature for modified function"; + return false; + } + + FunctionModification mod; + m_currentSignature = mod.signature = signature; + + QString access = attributes["access"].toLower(); + if (!access.isEmpty()) { + if (access == QLatin1String("private")) + mod.modifiers |= Modification::Private; + else if (access == QLatin1String("protected")) + mod.modifiers |= Modification::Protected; + else if (access == QLatin1String("public")) + mod.modifiers |= Modification::Public; + else if (access == QLatin1String("final")) + mod.modifiers |= Modification::Final; + else if (access == QLatin1String("non-final")) + mod.modifiers |= Modification::NonFinal; + else { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + } + + if (convertBoolean(attributes["deprecated"], "deprecated", false)) + mod.modifiers |= Modification::Deprecated; + + + QString remove = attributes["remove"].toLower(); + if (!remove.isEmpty()) { + if (remove == QLatin1String("all")) + mod.removal = TypeSystem::All; + else if (remove == QLatin1String("target")) + mod.removal = TypeSystem::TargetLangAndNativeCode; + else { + m_error = QString::fromLatin1("Bad removal type '%1'").arg(remove); + return false; + } + } + + QString rename = attributes["rename"]; + if (!rename.isEmpty()) { + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + } + + QString association = attributes["associated-to"]; + if (!association.isEmpty()) + mod.association = association; + + mod.setIsThread(convertBoolean(attributes["thread"], "thread", false)); + mod.setAllowThread(convertBoolean(attributes["allow-thread"], "allow-thread", false)); + + mod.modifiers |= (convertBoolean(attributes["virtual-slot"], "virtual-slot", false) ? Modification::VirtualSlot : 0); + + m_functionMods << mod; + } + break; + case StackElement::ReplaceDefaultExpression: + if (!(topElement.type & StackElement::ModifyArgument)) { + m_error = "Replace default expression only allowed as child of argument modification"; + return false; + } + + if (attributes["with"].isEmpty()) { + m_error = "Default expression replaced with empty string. Use remove-default-expression instead."; + return false; + } + + m_functionMods.last().argument_mods.last().replacedDefaultExpression = attributes["with"]; + break; + case StackElement::RemoveDefaultExpression: + m_functionMods.last().argument_mods.last().removedDefaultExpression = true; + break; + case StackElement::CustomMetaConstructor: + case StackElement::CustomMetaDestructor: { + CustomFunction *func = new CustomFunction(attributes["name"]); + func->paramName = attributes["param-name"]; + element->value.customFunction = func; + } + break; +#if 0 + case StackElement::ReferenceCount: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "reference-count must be child of modify-argument"; + return false; + } + + ReferenceCount rc; + + static QHash actions; + if (actions.isEmpty()) { + actions["add"] = ReferenceCount::Add; + actions["add-all"] = ReferenceCount::AddAll; + actions["remove"] = ReferenceCount::Remove; + actions["set"] = ReferenceCount::Set; + actions["ignore"] = ReferenceCount::Ignore; + } + rc.action = actions.value(attributes["action"].toLower(), ReferenceCount::Invalid); + + if (rc.action == ReferenceCount::Invalid) { + m_error = "unrecognized value for action attribute. supported actions:"; + foreach (QString action, actions.keys()) + m_error += " " + action; + } + + m_functionMods.last().argument_mods.last().referenceCounts.append(rc); + } + break; +#endif + + case StackElement::ParentOwner: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "parent-policy must be child of modify-argument"; + return false; + } + + ArgumentOwner ao; + + QString index = attributes["index"]; + if (index == "return") + index = "0"; + else if (index == "this") + index = "-1"; + + bool ok = false; + int idx = index.toInt(&ok); + if (!ok) { + m_error = QString("Cannot convert '%1' to integer").arg(index); + return false; + } + + static QHash actions; + if (actions.isEmpty()) { + actions["add"] = ArgumentOwner::Add; + actions["remove"] = ArgumentOwner::Remove; + } + + ao.action = actions.value(attributes["action"].toLower(), ArgumentOwner::Invalid); + if (!ao.action) { + m_error = QString("Invalid parent actionr"); + return false; + } + ao.index = idx; + m_functionMods.last().argument_mods.last().owner = ao; + } + break; + + + case StackElement::InjectCode: { + if (((!topElement.type & StackElement::ComplexTypeEntryMask)) + && (topElement.type != StackElement::ModifyFunction) + && (topElement.type != StackElement::Root)) { + m_error = "wrong parent type for code injection"; + return false; + } + + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangCode; // em algum lugar do cpp + languageNames["native"] = TypeSystem::NativeCode; // em algum lugar do cpp + languageNames["shell"] = TypeSystem::ShellCode; // coloca no header, mas antes da declaracao da classe + languageNames["shell-declaration"] = TypeSystem::ShellDeclaration; // coloca no header, dentro da declaracao da classe + languageNames["library-initializer"] = TypeSystem::PackageInitializer; + languageNames["destructor-function"] = TypeSystem::DestructorFunction; + languageNames["constructors"] = TypeSystem::Constructors; + languageNames["interface"] = TypeSystem::Interface; + } + + QString className = attributes["class"].toLower(); + if (!languageNames.contains(className)) { + m_error = QString("Invalid class specifier: '%1'").arg(className); + return false; + } + + + static QHash positionNames; + if (positionNames.isEmpty()) { + positionNames["beginning"] = CodeSnip::Beginning; + positionNames["end"] = CodeSnip::End; + // QtScript + positionNames["declaration"] = CodeSnip::Declaration; + positionNames["prototype-initialization"] = CodeSnip::PrototypeInitialization; + positionNames["constructor-initialization"] = CodeSnip::ConstructorInitialization; + positionNames["constructor"] = CodeSnip::Constructor; + } + + QString position = attributes["position"].toLower(); + if (!positionNames.contains(position)) { + m_error = QString("Invalid position: '%1'").arg(position); + return false; + } + + CodeSnip snip; + snip.language = languageNames[className]; + snip.position = positionNames[position]; + bool in_file = false; + + QString file_name = attributes["file"]; + + //Handler constructor.... + if (m_generate != TypeEntry::GenerateForSubclass && + m_generate != TypeEntry::GenerateNothing && + !file_name.isEmpty()) { + if (QFile::exists(file_name)) { + QFile code_file(file_name); + if (code_file.open(QIODevice::ReadOnly)) { + QString content; + content = code_file.readAll(); + content.prepend("// ========================================================================\n" + "// START of custom code block [file: " + file_name + "]\n"); + content.append("\n// END of custom code block [file: " + file_name + "]\n" + "// ========================================================================\n"); + snip.addCode(content); + in_file = true; + } + } else + ReportHandler::warning("File for inject code not exist: " + file_name); + + } + + if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) { + m_error = "Interface code injections must be direct child of an interface type entry"; + return false; + } + + if (topElement.type == StackElement::ModifyFunction) { + FunctionModification mod = m_functionMods.last(); + if (snip.language == TypeSystem::ShellDeclaration) { + m_error = "no function implementation in shell declaration in which to inject code"; + return false; + } + + m_functionMods.last().snips << snip; + if (in_file) + m_functionMods.last().modifiers |= FunctionModification::CodeInjection; + element->type = StackElement::InjectCodeInFunction; + + } else if (topElement.type == StackElement::Root) { + ((TypeSystemTypeEntry *) element->entry)->addCodeSnip(snip); + + } else if (topElement.type != StackElement::Root) + m_codeSnips << snip; + + } + break; + case StackElement::Include: { + QString location = attributes["location"].toLower(); + + static QHash locationNames; + if (locationNames.isEmpty()) { + locationNames["global"] = Include::IncludePath; + locationNames["local"] = Include::LocalPath; + locationNames["target"] = Include::TargetLangImport; + } + + if (!locationNames.contains(location)) { + m_error = QString("Location not recognized: '%1'").arg(location); + return false; + } + + Include::IncludeType loc = locationNames[location]; + Include inc(loc, attributes["file-name"]); + + ComplexTypeEntry *ctype = static_cast(element->entry); + if (topElement.type & StackElement::ComplexTypeEntryMask) + ctype->setInclude(inc); + else if (topElement.type == StackElement::ExtraIncludes) + ctype->addExtraInclude(inc); + else { + m_error = "Only supported parents are complex types and extra-includes"; + return false; + } + + inc = ctype->include(); + IncludeList lst = ctype->extraIncludes(); + ctype = ctype->designatedInterface(); + if (ctype) { + ctype->setExtraIncludes(lst); + ctype->setInclude(inc); + } + } + break; + case StackElement::Rejection: { + QString cls = attributes["class"]; + QString function = attributes["function-name"]; + QString field = attributes["field-name"]; + QString enum_ = attributes["enum-name"]; + if (cls == "*" && function == "*" && field == "*" && enum_ == "*") { + m_error = "bad reject entry, neither 'class', 'function-name' nor " + "'field' specified"; + return false; + } + m_database->addRejection(cls, function, field, enum_); + } + break; + case StackElement::Template: + element->value.templateEntry = new TemplateEntry(attributes["name"]); + break; + case StackElement::TemplateInstanceEnum: + if (!(topElement.type & StackElement::CodeSnipMask) && + (topElement.type != StackElement::Template) && + (topElement.type != StackElement::CustomMetaConstructor) && + (topElement.type != StackElement::CustomMetaDestructor) && + (topElement.type != StackElement::ConversionRule)) { + m_error = "Can only insert templates into code snippets, templates, custom-constructors, custom-destructors or conversion-rule."; + return false; + } + element->value.templateInstance = new TemplateInstance(attributes["name"]); + break; + case StackElement::Replace: + if (topElement.type != StackElement::TemplateInstanceEnum) { + m_error = "Can only insert replace rules into insert-template."; + return false; + } + element->parent->value.templateInstance->addReplaceRule(attributes["from"], attributes["to"]); + break; + default: + break; // nada + }; + } + + current = element; + return true; +} + +TypeDatabase *TypeDatabase::instance() +{ + static TypeDatabase *db = new TypeDatabase(); + return db; +} + +TypeDatabase::TypeDatabase() : m_suppressWarnings(true) +{ + StringTypeEntry* e = new StringTypeEntry("QXmlStreamStringRef"); + e->setPreferredConversion(false); + addType(e); + + addType(new VoidTypeEntry()); +} + +QString TypeDatabase::modifiedTypesystemFilepath(const QString &ts_file) +{ + if (!QFile::exists(ts_file)) { + foreach (const QString &path, m_typesystemPaths) { + QString filepath(path + '/' + ts_file); + if (QFile::exists(filepath)) + return filepath; + } + } + return ts_file; +} + +bool TypeDatabase::parseFile(const QString &filename, bool generate) +{ + QString filepath; + if (filename[0] != '/') + filepath = modifiedTypesystemFilepath(filename); + else + filepath = filename; + + if (m_parsedTypesystemFiles.contains(filepath)) + return m_parsedTypesystemFiles[filepath]; + + QFile file(filepath); + Q_ASSERT_X(file.exists(), __FUNCTION__, ("Can't find " + filename).toLocal8Bit().data()); + + int count = m_entries.size(); + bool ok = parseFile(&file, generate); + m_parsedTypesystemFiles[filepath] = ok; + int newCount = m_entries.size(); + + ReportHandler::debugSparse(QString::fromLatin1("Parsed: '%1', %2 new entries") + .arg(filename) + .arg(newCount - count)); + return ok; +} + +bool TypeDatabase::parseFile(QIODevice* device, bool generate) +{ + QXmlInputSource source(device); + QXmlSimpleReader reader; + Handler handler(this, generate); + + reader.setContentHandler(&handler); + reader.setErrorHandler(&handler); + + return reader.parse(&source, false); +} + +QString PrimitiveTypeEntry::javaObjectName() const +{ + static QHash table; + if (table.isEmpty()) { + table["boolean"] = "Boolean"; + table["byte"] = "Byte"; + table["char"] = "Character"; + table["short"] = "Short"; + table["int"] = "Integer"; + table["long"] = "Long"; + table["float"] = "Float"; + table["double"] = "Double"; + } + Q_ASSERT(table.contains(targetLangName())); + return table[targetLangName()]; +} + +PrimitiveTypeEntry* PrimitiveTypeEntry::basicAliasedTypeEntry() const +{ + if (!m_aliasedTypeEntry) + return 0; + + PrimitiveTypeEntry* baseAliasTypeEntry = m_aliasedTypeEntry->basicAliasedTypeEntry(); + if (baseAliasTypeEntry) + return baseAliasTypeEntry; + else + return m_aliasedTypeEntry; +} + +ContainerTypeEntry *TypeDatabase::findContainerType(const QString &name) +{ + QString template_name = name; + + int pos = name.indexOf('<'); + if (pos > 0) + template_name = name.left(pos); + + TypeEntry *type_entry = findType(template_name); + if (type_entry && type_entry->isContainer()) + return static_cast(type_entry); + return 0; +} + +PrimitiveTypeEntry *TypeDatabase::findTargetLangPrimitiveType(const QString &targetLangName) +{ + foreach (QList entries, m_entries.values()) { + foreach (TypeEntry *e, entries) { + if (e && e->isPrimitive()) { + PrimitiveTypeEntry *pe = static_cast(e); + if (pe->targetLangName() == targetLangName && pe->preferredConversion()) + return pe; + } + } + } + + return 0; +} + +IncludeList TypeDatabase::extraIncludes(const QString &className) +{ + ComplexTypeEntry *typeEntry = findComplexType(className); + if (typeEntry) + return typeEntry->extraIncludes(); + else + return IncludeList(); +} + + + +QString Include::toString() const +{ + if (type == IncludePath) + return "#include <" + name + '>'; + else if (type == LocalPath) + return "#include \"" + name + "\""; + else + return "import " + name + ";"; +} + +QString Modification::accessModifierString() const +{ + if (isPrivate()) return "private"; + if (isProtected()) return "protected"; + if (isPublic()) return "public"; + if (isFriendly()) return "friendly"; + return QString(); +} + +FunctionModificationList ComplexTypeEntry::functionModifications(const QString &signature) const +{ + FunctionModificationList lst; + for (int i = 0; i < m_functionMods.count(); ++i) { + const FunctionModification &mod = m_functionMods.at(i); + if (mod.signature == signature) + lst << mod; + + } + + return lst; +} + +FieldModification ComplexTypeEntry::fieldModification(const QString &name) const +{ + for (int i = 0; i < m_fieldMods.size(); ++i) + if (m_fieldMods.at(i).name == name) + return m_fieldMods.at(i); + FieldModification mod; + mod.name = name; + mod.modifiers = FieldModification::Readable | FieldModification::Writable; + return mod; +} + +QString ContainerTypeEntry::targetLangPackage() const +{ + if (m_type == PairContainer) + return "com.trolltech.qt"; + return "java.util"; +} + +QString ContainerTypeEntry::targetLangName() const +{ + + switch (m_type) { + case StringListContainer: return "QStringList"; + case ListContainer: return "QList"; + case LinkedListContainer: return "LinkedList"; + case VectorContainer: return "QVector"; + case StackContainer: return "QStack"; + case QueueContainer: return "QQueue"; + case SetContainer: return "QSet"; + case MapContainer: return "QMap"; + case MultiMapContainer: return "QMultiMap"; + case HashContainer: return "QHashMap"; + //case MultiHashCollectio: return "MultiHash"; + case PairContainer: return "QPair"; + default: + qWarning("bad type... %d", m_type); + break; + } + return QString(); +} + +QString ContainerTypeEntry::qualifiedCppName() const +{ + if (m_type == StringListContainer) + return "QStringList"; + return ComplexTypeEntry::qualifiedCppName(); +} + +QString EnumTypeEntry::targetLangQualifier() const +{ + TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier); + if (te) + return te->targetLangName(); + else + return m_qualifier; +} + +QString EnumTypeEntry::targetLangApiName() const +{ + return "jint"; +} + +QString FlagsTypeEntry::targetLangApiName() const +{ + return "jint"; +} + +void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue) +{ + m_enumRedirections << EnumValueRedirection(rejected, usedValue); +} + +QString EnumTypeEntry::enumValueRedirection(const QString &value) const +{ + for (int i = 0; i < m_enumRedirections.size(); ++i) + if (m_enumRedirections.at(i).rejected == value) + return m_enumRedirections.at(i).used; + return QString(); +} + +QString FlagsTypeEntry::qualifiedTargetLangName() const +{ + return targetLangPackage() + "." + m_enum->targetLangQualifier() + "." + targetLangName(); +} + + +void TypeDatabase::addRejection(const QString &class_name, const QString &function_name, + const QString &field_name, const QString &enum_name) +{ + TypeRejection r; + r.class_name = class_name; + r.function_name = function_name; + r.field_name = field_name; + r.enum_name = enum_name; + + m_rejections << r; +} + +bool TypeDatabase::isClassRejected(const QString &class_name) +{ + if (!m_rebuildClasses.isEmpty()) + return !m_rebuildClasses.contains(class_name); + + foreach (const TypeRejection &r, m_rejections) + if (r.class_name == class_name && r.function_name == "*" && r.field_name == "*" && r.enum_name == "*") + return true; + + return false; +} + +bool TypeDatabase::isEnumRejected(const QString &class_name, const QString &enum_name) +{ + foreach (const TypeRejection &r, m_rejections) { + if (r.enum_name == enum_name + && (r.class_name == class_name || r.class_name == "*")) { + return true; + } + } + + return false; +} + +bool TypeDatabase::isFunctionRejected(const QString &class_name, const QString &function_name) +{ + foreach (const TypeRejection &r, m_rejections) + if (r.function_name == function_name && + (r.class_name == class_name || r.class_name == "*")) + return true; + return false; +} + + +bool TypeDatabase::isFieldRejected(const QString &class_name, const QString &field_name) +{ + foreach (const TypeRejection &r, m_rejections) + if (r.field_name == field_name && + (r.class_name == class_name || r.class_name == "*")) + return true; + return false; +} + +FlagsTypeEntry *TypeDatabase::findFlagsType(const QString &name) const +{ + FlagsTypeEntry *fte = (FlagsTypeEntry *) findType(name); + return fte ? fte : (FlagsTypeEntry *) m_flagsEntries.value(name); +} + +QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/) +{ + return QLatin1String("Global"); +} + + +/*! + * The Visual Studio 2002 compiler doesn't support these symbols, + * which our typedefs unforntuatly expand to. + */ +QString fixCppTypeName(const QString &name) +{ + if (name == "long long") return "qint64"; + else if (name == "unsigned long long") return "quint64"; + return name; +} + +QString formattedCodeHelper(QTextStream &s, Indentor &indentor, QStringList &lines) +{ + bool multilineComment = false; + bool lastEmpty = true; + QString lastLine; + while (!lines.isEmpty()) { + const QString line = lines.takeFirst().trimmed(); + if (line.isEmpty()) { + if (!lastEmpty) + s << endl; + lastEmpty = true; + continue; + } else + lastEmpty = false; + + if (line.startsWith("/*")) + multilineComment = true; + + if (multilineComment) { + s << indentor; + if (line.startsWith("*")) + s << " "; + s << line << endl; + if (line.endsWith("*/")) + multilineComment = false; + } else if (line.startsWith("}")) + return line; + else if (line.endsWith("")) { + s << indentor << line << endl; + return 0; + } else if (line.endsWith("{")) { + s << indentor << line << endl; + QString tmp; + { + Indentation indent(indentor); + tmp = formattedCodeHelper(s, indentor, lines); + } + if (!tmp.isNull()) + s << indentor << tmp << endl; + + lastLine = tmp; + continue; + } else { + s << indentor; + if (!lastLine.isEmpty() && + !lastLine.endsWith(";") && + !line.startsWith("@") && + !line.startsWith("//") && + !lastLine.startsWith("//") && + !lastLine.endsWith("}") && + !line.startsWith("{")) + s << " "; + s << line << endl; + } + lastLine = line; + } + return 0; +} + + +QTextStream &CodeSnip::formattedCode(QTextStream &s, Indentor &indentor) const +{ + QStringList lst(code().split("\n")); + while (!lst.isEmpty()) { + QString tmp = formattedCodeHelper(s, indentor, lst); + if (!tmp.isNull()) + s << indentor << tmp << endl; + + } + s.flush(); + return s; +} + +QString TemplateInstance::expandCode() const +{ + TemplateEntry *templateEntry = TypeDatabase::instance()->findTemplate(m_name); + if (templateEntry) { + QString res = templateEntry->code(); + foreach (QString key, replaceRules.keys()) + res.replace(key, replaceRules[key]); + + return "// TEMPLATE - " + m_name + " - START" + res + "// TEMPLATE - " + m_name + " - END"; + } else + ReportHandler::warning("insert-template referring to non-existing template '" + m_name + "'"); + + return QString(); +} + + +QString CodeSnipAbstract::code() const +{ + QString res; + foreach (CodeSnipFragment *codeFrag, codeList) + res.append(codeFrag->code()); + + return res; +} + +QString CodeSnipFragment::code() const +{ + if (m_instance) + return m_instance->expandCode(); + else + return m_code; +} + +QString FunctionModification::toString() const +{ + QString str = signature + QLatin1String("->"); + if (modifiers & AccessModifierMask) { + switch (modifiers & AccessModifierMask) { + case Private: str += QLatin1String("private"); break; + case Protected: str += QLatin1String("protected"); break; + case Public: str += QLatin1String("public"); break; + case Friendly: str += QLatin1String("friendly"); break; + } + } + + if (modifiers & Final) str += QLatin1String("final"); + if (modifiers & NonFinal) str += QLatin1String("non-final"); + + if (modifiers & Readable) str += QLatin1String("readable"); + if (modifiers & Writable) str += QLatin1String("writable"); + + if (modifiers & CodeInjection) { + foreach (CodeSnip s, snips) { + str += QLatin1String("\n//code injection:\n"); + str += s.code(); + } + } + + if (modifiers & Rename) str += QLatin1String("renamed:") + renamedToName; + + if (modifiers & Deprecated) str += QLatin1String("deprecate"); + + if (modifiers & ReplaceExpression) str += QLatin1String("replace-expression"); + + return str; +} + +/* +static void injectCode(ComplexTypeEntry *e, + const char *signature, + const QByteArray &code, + const ArgumentMap &args) +{ + CodeSnip snip; + snip.language = TypeSystem::NativeCode; + snip.position = CodeSnip::Beginning; + snip.addCode(QString::fromLatin1(code)); + snip.argumentMap = args; + + FunctionModification mod; + mod.signature = QMetaObject::normalizedSignature(signature); + mod.snips << snip; + mod.modifiers = Modification::CodeInjection; + e->addFunctionModification(mod); +} +*/ + diff --git a/typesystem.h b/typesystem.h new file mode 100644 index 000000000..f03fd04ee --- /dev/null +++ b/typesystem.h @@ -0,0 +1,1958 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef TYPESYSTEM_H +#define TYPESYSTEM_H + +#include +#include +#include +#include +#include + +class Indentor; + +class AbstractMetaType; +class QTextStream; + +class EnumTypeEntry; +class FlagsTypeEntry; + +extern QString strings_Object; +extern QString strings_String; +extern QString strings_Thread; +extern QString strings_char; +extern QString stringsJavaLang; +extern QString strings_jchar; +extern QString strings_jobject; + +struct Include +{ + enum IncludeType { + IncludePath, + LocalPath, + TargetLangImport + }; + + Include() : type(IncludePath) {} + Include(IncludeType t, const QString &nam) : type(t), name(nam) {}; + + bool isValid() + { + return !name.isEmpty(); + } + + IncludeType type; + QString name; + + QString toString() const; + + bool operator<(const Include &other) const + { + return name < other.name; + } +}; +typedef QList IncludeList; + +typedef QMap ArgumentMap; + +class TemplateInstance; + +namespace TypeSystem +{ +enum Language { + NoLanguage = 0x0000, + TargetLangCode = 0x0001, + NativeCode = 0x0002, + ShellCode = 0x0004, + ShellDeclaration = 0x0008, + PackageInitializer = 0x0010, + DestructorFunction = 0x0020, + Constructors = 0x0040, + Interface = 0x0080, + + // masks + All = TargetLangCode + | NativeCode + | ShellCode + | ShellDeclaration + | PackageInitializer + | Constructors + | Interface + | DestructorFunction, + + TargetLangAndNativeCode = TargetLangCode | NativeCode +}; + +enum Ownership { + InvalidOwnership, + DefaultOwnership, + TargetLangOwnership, + CppOwnership +}; +}; + +struct ReferenceCount +{ + ReferenceCount() {} + enum Action { // 0x01 - 0xff + Invalid = 0x00, + Add = 0x01, + AddAll = 0x02, + Remove = 0x04, + Set = 0x08, + Ignore = 0x10, + + ActionsMask = 0xff, + + Padding = 0xffffffff + }; + + Action action; +}; + +struct ArgumentOwner +{ + enum Action { + Invalid = 0x00, + Add = 0x01, + Remove = 0x02 + }; + ArgumentOwner() : action(ArgumentOwner::Invalid), index(-2) {} + + Action action; + int index; +}; + +class CodeSnipFragment +{ +private: + const QString m_code; + TemplateInstance *m_instance; + +public: + CodeSnipFragment(const QString &code) + : m_code(code), + m_instance(0) {} + + CodeSnipFragment(TemplateInstance *instance) + : m_instance(instance) {} + + QString code() const; +}; + +class CodeSnipAbstract +{ +public: + QString code() const; + + void addCode(const QString &code) + { + codeList.append(new CodeSnipFragment(code)); + } + + void addTemplateInstance(TemplateInstance *ti) + { + codeList.append(new CodeSnipFragment(ti)); + } + + QList codeList; +}; + +class CustomFunction : public CodeSnipAbstract +{ +public: + CustomFunction(const QString &n = QString()) : name(n) { } + + QString name; + QString paramName; +}; + +class TemplateEntry : public CodeSnipAbstract +{ +public: + TemplateEntry(const QString &name) + : m_name(name) + { + }; + + QString name() const + { + return m_name; + }; + +private: + QString m_name; +}; + +typedef QHash TemplateEntryHash; + +class TemplateInstance +{ +public: + TemplateInstance(const QString &name) + : m_name(name) {} + + void addReplaceRule(const QString &name, const QString &value) + { + replaceRules[name] = value; + } + + QString expandCode() const; + + QString name() const + { + return m_name; + } + +private: + const QString m_name; + QHash replaceRules; +}; + + +class CodeSnip : public CodeSnipAbstract +{ +public: + enum Position { + Beginning, + End, + AfterThis, + // QtScript + Declaration, + PrototypeInitialization, + ConstructorInitialization, + Constructor + }; + + CodeSnip() : language(TypeSystem::TargetLangCode) { } + CodeSnip(TypeSystem::Language lang) : language(lang) { } + + // Very simple, easy to make code ugly if you try + QTextStream &formattedCode(QTextStream &s, Indentor &indentor) const; + + TypeSystem::Language language; + Position position; + ArgumentMap argumentMap; +}; +typedef QList CodeSnipList; + +struct ArgumentModification +{ + ArgumentModification(int idx) + : removedDefaultExpression(false), removed(false), + noNullPointers(false), index(idx) {} + + // Should the default expression be removed? + uint removedDefaultExpression : 1; + uint removed : 1; + uint noNullPointers : 1; + uint resetAfterUse : 1; + + // The index of this argument + int index; + + // Reference count flags for this argument + QList referenceCounts; + + // The text given for the new type of the argument + QString modified_type; + + QString replace_value; + + // The code to be used to construct a return value when noNullPointers is true and + // the returned value is null. If noNullPointers is true and this string is + // empty, then the base class implementation will be used (or a default construction + // if there is no implementation) + QString nullPointerDefaultValue; + + // The text of the new default expression of the argument + QString replacedDefaultExpression; + + // The new definition of ownership for a specific argument + QHash ownerships; + + // Different conversion rules + CodeSnipList conversion_rules; + + //QObject parent(owner) of this argument + ArgumentOwner owner; +}; + +struct Modification +{ + enum Modifiers { + Private = 0x0001, + Protected = 0x0002, + Public = 0x0003, + Friendly = 0x0004, + AccessModifierMask = 0x000f, + + Final = 0x0010, + NonFinal = 0x0020, + FinalMask = Final | NonFinal, + + Readable = 0x0100, + Writable = 0x0200, + + CodeInjection = 0x1000, + Rename = 0x2000, + Deprecated = 0x4000, + ReplaceExpression = 0x8000, + VirtualSlot = 0x10000 | NonFinal + }; + + Modification() : modifiers(0) { } + + bool isAccessModifier() const + { + return modifiers & AccessModifierMask; + } + Modifiers accessModifier() const + { + return Modifiers(modifiers & AccessModifierMask); + } + bool isPrivate() const + { + return accessModifier() == Private; + } + bool isProtected() const + { + return accessModifier() == Protected; + } + bool isPublic() const + { + return accessModifier() == Public; + } + bool isFriendly() const + { + return accessModifier() == Friendly; + } + bool isFinal() const + { + return modifiers & Final; + } + bool isNonFinal() const + { + return modifiers & NonFinal; + } + bool isVirtualSlot() const + { + return (modifiers & VirtualSlot) == VirtualSlot; + } + QString accessModifierString() const; + + bool isDeprecated() const + { + return modifiers & Deprecated; + } + + void setRenamedTo(const QString &name) + { + renamedToName = name; + } + QString renamedTo() const + { + return renamedToName; + } + bool isRenameModifier() const + { + return modifiers & Rename; + } + + uint modifiers; + QString renamedToName; +}; + +struct FunctionModification: public Modification +{ + FunctionModification() : removal(TypeSystem::NoLanguage), m_thread(false), m_allowThread(false) {} + + bool isCodeInjection() const + { + return modifiers & CodeInjection; + } + bool isRemoveModifier() const + { + return removal != TypeSystem::NoLanguage; + } + void setIsThread(bool flag) + { + m_thread = flag; + } + bool isThread() const + { + return m_thread; + } + bool allowThread() const + { + return m_allowThread; + } + void setAllowThread(bool allow) + { + m_allowThread = allow; + } + + QString toString() const; + + QString signature; + QString association; + CodeSnipList snips; + TypeSystem::Language removal; + + QList argument_mods; + +private: + bool m_thread; + bool m_allowThread; + +}; +typedef QList FunctionModificationList; + +struct FieldModification: public Modification +{ + bool isReadable() const + { + return modifiers & Readable; + } + bool isWritable() const + { + return modifiers & Writable; + } + + QString name; +}; +typedef QList FieldModificationList; + +struct ExpensePolicy +{ + ExpensePolicy() : limit(-1) {} + int limit; + QString cost; + bool isValid() const + { + return limit >= 0; + } +}; + +class InterfaceTypeEntry; +class ObjectTypeEntry; + +class DocModification +{ +public: + enum Mode { + Append, + Prepend, + Replace, + XPathReplace + }; + + DocModification(const QString& xpath, const QString& signature) + : format(TypeSystem::NativeCode), m_mode(XPathReplace), + m_xpath(xpath), m_signature(signature) {} + DocModification(Mode mode, const QString& signature) + : m_mode(mode), m_signature(signature) {} + + void setCode(const QString& code) + { + m_code = code; + } + QString code() const + { + return m_code; + } + QString xpath() const + { + return m_xpath; + } + QString signature() const + { + return m_signature; + } + Mode mode() const + { + return m_mode; + } + + TypeSystem::Language format; + +private: + Mode m_mode; + QString m_code; + QString m_xpath; + QString m_signature; +}; + +typedef QList DocModificationList; + +class TypeEntry +{ +public: + enum Type { + PrimitiveType, + VoidType, + FlagsType, + EnumType, + TemplateArgumentType, + ThreadType, + BasicValueType, + StringType, + ContainerType, + InterfaceType, + ObjectType, + NamespaceType, + VariantType, + JObjectWrapperType, + CharType, + ArrayType, + TypeSystemType, + CustomType, + }; + + enum CodeGeneration { + GenerateTargetLang = 0x0001, + GenerateCpp = 0x0002, + GenerateForSubclass = 0x0004, + + GenerateNothing = 0, + GenerateAll = 0xffff, + GenerateCode = GenerateTargetLang | GenerateCpp + }; + + TypeEntry(const QString &name, Type t) + : m_name(name), + m_type(t), + m_codeGeneration(GenerateAll), + m_preferredConversion(true) + { + }; + + virtual ~TypeEntry() { } + + Type type() const + { + return m_type; + } + bool isPrimitive() const + { + return m_type == PrimitiveType; + } + bool isEnum() const + { + return m_type == EnumType; + } + bool isFlags() const + { + return m_type == FlagsType; + } + bool isInterface() const + { + return m_type == InterfaceType; + } + bool isObject() const + { + return m_type == ObjectType; + } + bool isString() const + { + return m_type == StringType; + } + bool isChar() const + { + return m_type == CharType; + } + bool isNamespace() const + { + return m_type == NamespaceType; + } + bool isContainer() const + { + return m_type == ContainerType; + } + bool isVariant() const + { + return m_type == VariantType; + } + bool isJObjectWrapper() const + { + return m_type == JObjectWrapperType; + } + bool isArray() const + { + return m_type == ArrayType; + } + bool isTemplateArgument() const + { + return m_type == TemplateArgumentType; + } + bool isVoid() const + { + return m_type == VoidType; + } + bool isThread() const + { + return m_type == ThreadType; + } + bool isCustom() const + { + return m_type == CustomType; + } + bool isBasicValue() const + { + return m_type == BasicValueType; + } + bool isTypeSystem() const + { + return m_type == TypeSystemType; + } + + virtual bool preferredConversion() const + { + return m_preferredConversion; + } + virtual void setPreferredConversion(bool b) + { + m_preferredConversion = b; + } + + // The type's name in C++, fully qualified + QString name() const + { + return m_name; + } + + uint codeGeneration() const + { + return m_codeGeneration; + } + void setCodeGeneration(uint cg) + { + m_codeGeneration = cg; + } + + // Returns true if code must be generated for this entry, + // it will return false in case of types coming from typesystems + // included for reference only. + // NOTE: 'GenerateForSubclass' means 'generate="no"' + // on 'load-typesystem' tag + inline bool generateCode() const + { + return m_codeGeneration != TypeEntry::GenerateForSubclass + && m_codeGeneration != TypeEntry::GenerateNothing; + } + + virtual QString qualifiedCppName() const + { + return m_name; + } + + /** + * Its type's name in target language API + * The target language API name represents how this type is + * referred on low level code for the target language. + * Examples: for Java this would be a JNI name, for Python + * it should represent the CPython type name. + * /return string representing the target language API name + * for this type entry + */ + virtual QString targetLangApiName() const + { + return m_name; + } + + // The type's name in TargetLang + virtual QString targetLangName() const + { + return m_name; + } + + // The type to lookup when converting to TargetLang + virtual QString lookupName() const + { + return targetLangName(); + } + + // The package + virtual QString targetLangPackage() const + { + return QString(); + } + + virtual QString qualifiedTargetLangName() const + { + QString pkg = targetLangPackage(); + if (pkg.isEmpty()) + return targetLangName(); + return pkg + '.' + targetLangName(); + } + + virtual InterfaceTypeEntry *designatedInterface() const + { + return 0; + } + + void setCustomConstructor(const CustomFunction &func) + { + m_customConstructor = func; + } + CustomFunction customConstructor() const + { + return m_customConstructor; + } + + void setCustomDestructor(const CustomFunction &func) + { + m_customDestructor = func; + } + CustomFunction customDestructor() const + { + return m_customDestructor; + } + + virtual bool isValue() const + { + return false; + } + virtual bool isComplex() const + { + return false; + } + + virtual bool isNativeIdBased() const + { + return false; + } + + CodeSnipList codeSnips() const + { + return m_codeSnips; + } + void setCodeSnips(const CodeSnipList &codeSnips) + { + m_codeSnips = codeSnips; + } + void addCodeSnip(const CodeSnip &codeSnip) + { + m_codeSnips << codeSnip; + } + + void setDocModification(const DocModificationList& docMods) + { + m_docModifications << docMods; + } + DocModificationList docModifications() const + { + return m_docModifications; + } + +private: + QString m_name; + Type m_type; + uint m_codeGeneration; + CustomFunction m_customConstructor; + CustomFunction m_customDestructor; + bool m_preferredConversion; + CodeSnipList m_codeSnips; + DocModificationList m_docModifications; +}; +typedef QHash > TypeEntryHash; +typedef QHash SingleTypeEntryHash; + + +class TypeSystemTypeEntry : public TypeEntry +{ +public: + TypeSystemTypeEntry(const QString &name) + : TypeEntry(name, TypeSystemType) + { + }; +}; + + +class ThreadTypeEntry : public TypeEntry +{ +public: + ThreadTypeEntry() : TypeEntry("QThread", ThreadType) + { + setCodeGeneration(GenerateNothing); + } + + QString targetLangApiName() const + { + return strings_jobject; + } + QString targetLangName() const + { + return strings_Thread; + } + QString targetLangPackage() const + { + return stringsJavaLang; + } +}; + +class VoidTypeEntry : public TypeEntry +{ +public: + VoidTypeEntry() : TypeEntry("void", VoidType) { } +}; + +class TemplateArgumentEntry : public TypeEntry +{ +public: + TemplateArgumentEntry(const QString &name) + : TypeEntry(name, TemplateArgumentType), m_ordinal(0) + { + } + + int ordinal() const + { + return m_ordinal; + } + void setOrdinal(int o) + { + m_ordinal = o; + } + +private: + int m_ordinal; +}; + +class ArrayTypeEntry : public TypeEntry +{ +public: + ArrayTypeEntry(const TypeEntry *nested_type) + : TypeEntry("Array", ArrayType), m_nestedType(nested_type) + { + Q_ASSERT(m_nestedType); + } + + void setNestedTypeEntry(TypeEntry *nested) + { + m_nestedType = nested; + } + const TypeEntry *nestedTypeEntry() const + { + return m_nestedType; + } + + QString targetLangName() const + { + return m_nestedType->targetLangName() + "[]"; + } + QString targetLangApiName() const + { + if (m_nestedType->isPrimitive()) + return m_nestedType->targetLangApiName() + "Array"; + else + return "jobjectArray"; + } + +private: + const TypeEntry *m_nestedType; +}; + + +class PrimitiveTypeEntry : public TypeEntry +{ +public: + PrimitiveTypeEntry(const QString &name) + : TypeEntry(name, PrimitiveType), + m_preferredConversion(true), + m_preferredTargetLangType(true), + m_aliasedTypeEntry(0) + { + } + + QString targetLangName() const + { + return m_targetLangName; + } + void setTargetLangName(const QString &targetLangName) + { + m_targetLangName = targetLangName; + } + + QString targetLangApiName() const + { + return m_targetLangApiName; + } + void setTargetLangApiName(const QString &targetLangApiName) + { + m_targetLangApiName = targetLangApiName; + } + + /** + * The PrimitiveTypeEntry pointed by this type entry if it + * represents an alias (i.e. a typedef). + * /return the type pointed by the alias, or a null pointer + * if the current object is not an alias + */ + PrimitiveTypeEntry* aliasedTypeEntry() const { return m_aliasedTypeEntry; } + + /** + * Defines type aliased by this entry. + * /param aliasedTypeEntry type aliased by this entry + */ + void setAliasedTypeEntry(PrimitiveTypeEntry* aliasedTypeEntry) + { + m_aliasedTypeEntry = aliasedTypeEntry; + } + + /** + * Finds the most basic primitive type that the typedef represents, + * i.e. a type that is not an alias. + * /return the most basic non-aliased primitive type represented + * by this typedef + */ + PrimitiveTypeEntry* basicAliasedTypeEntry() const; + + QString javaObjectFullName() const + { + return javaObjectPackage() + "." + javaObjectName(); + } + QString javaObjectName() const; + QString javaObjectPackage() const + { + return stringsJavaLang; + } + + virtual bool preferredConversion() const + { + return m_preferredConversion; + } + virtual void setPreferredConversion(bool b) + { + m_preferredConversion = b; + } + + virtual bool preferredTargetLangType() const + { + return m_preferredTargetLangType; + } + virtual void setPreferredTargetLangType(bool b) + { + m_preferredTargetLangType = b; + } + +private: + QString m_targetLangName; + QString m_targetLangApiName; + uint m_preferredConversion : 1; + uint m_preferredTargetLangType : 1; + PrimitiveTypeEntry* m_aliasedTypeEntry; +}; + + + + +struct EnumValueRedirection +{ + EnumValueRedirection(const QString &rej, const QString &us) + : rejected(rej), + used(us) + { + } + QString rejected; + QString used; +}; + +class EnumTypeEntry : public TypeEntry +{ +public: + EnumTypeEntry(const QString &nspace, const QString &enumName) + : TypeEntry(nspace.isEmpty() ? enumName : nspace + QLatin1String("::") + enumName, + EnumType), + m_flags(0), + m_extensible(false) + { + m_qualifier = nspace; + m_targetLangName = enumName; + } + + QString targetLangPackage() const + { + return m_packageName; + } + void setTargetLangPackage(const QString &package) + { + m_packageName = package; + } + + QString targetLangName() const + { + return m_targetLangName; + } + QString targetLangQualifier() const; + QString qualifiedTargetLangName() const + { + QString qualifiedName; + QString pkg = targetLangPackage(); + QString qualifier = targetLangQualifier(); + + if (!pkg.isEmpty()) + qualifiedName += pkg + '.'; + if (!qualifier.isEmpty()) + qualifiedName += qualifier + '.'; + qualifiedName += targetLangName(); + + return qualifiedName; + } + + QString targetLangApiName() const; + + QString qualifier() const + { + return m_qualifier; + } + void setQualifier(const QString &q) + { + m_qualifier = q; + } + + virtual bool preferredConversion() const + { + return false; + } + + bool isBoundsChecked() const + { + return m_lowerBound.isEmpty() && m_upperBound.isEmpty(); + } + + QString upperBound() const + { + return m_upperBound; + } + void setUpperBound(const QString &bound) + { + m_upperBound = bound; + } + + QString lowerBound() const + { + return m_lowerBound; + } + void setLowerBound(const QString &bound) + { + m_lowerBound = bound; + } + + void setFlags(FlagsTypeEntry *flags) + { + m_flags = flags; + } + FlagsTypeEntry *flags() const + { + return m_flags; + } + + bool isExtensible() const + { + return m_extensible; + } + void setExtensible(bool is) + { + m_extensible = is; + } + + bool isEnumValueRejected(const QString &name) + { + return m_rejectedEnums.contains(name); + } + void addEnumValueRejection(const QString &name) + { + m_rejectedEnums << name; + } + QStringList enumValueRejections() const + { + return m_rejectedEnums; + } + + void addEnumValueRedirection(const QString &rejected, const QString &usedValue); + QString enumValueRedirection(const QString &value) const; + + bool forceInteger() const + { + return m_forceInteger; + } + void setForceInteger(bool force) + { + m_forceInteger = force; + } + +private: + QString m_packageName; + QString m_qualifier; + QString m_targetLangName; + + QString m_lowerBound; + QString m_upperBound; + + QStringList m_rejectedEnums; + QList m_enumRedirections; + + FlagsTypeEntry *m_flags; + + bool m_extensible; + bool m_forceInteger; +}; + +class FlagsTypeEntry : public TypeEntry +{ +public: + FlagsTypeEntry(const QString &name) : TypeEntry(name, FlagsType), m_enum(0) + { + } + + QString qualifiedTargetLangName() const; + QString targetLangName() const + { + return m_targetLangName; + } + QString targetLangApiName() const; + virtual bool preferredConversion() const + { + return false; + } + + QString originalName() const + { + return m_originalName; + } + void setOriginalName(const QString &s) + { + m_originalName = s; + } + + QString flagsName() const + { + return m_targetLangName; + } + void setFlagsName(const QString &name) + { + m_targetLangName = name; + } + + bool forceInteger() const + { + return m_enum->forceInteger(); + } + + EnumTypeEntry *originator() const + { + return m_enum; + } + void setOriginator(EnumTypeEntry *e) + { + m_enum = e; + } + + QString targetLangPackage() const + { + return m_enum->targetLangPackage(); + } + +private: + QString m_originalName; + QString m_targetLangName; + EnumTypeEntry *m_enum; +}; + + +class ComplexTypeEntry : public TypeEntry +{ +public: + enum TypeFlag { + ForceAbstract = 0x1, + DeleteInMainThread = 0x2, + Deprecated = 0x4 + }; + typedef QFlags TypeFlags; + + enum CopyableFlag { + CopyableSet, + NonCopyableSet, + Unknown + }; + + ComplexTypeEntry(const QString &name, Type t) + : TypeEntry(QString(name).replace(".*::", ""), t), + m_qualifiedCppName(name), + m_qobject(false), + m_polymorphicBase(false), + m_genericClass(false), + m_typeFlags(0), + m_copyableFlag(Unknown), + m_hashFunction("") + { + } + + bool isComplex() const + { + return true; + } + + IncludeList extraIncludes() const + { + return m_extraIncludes; + } + void setExtraIncludes(const IncludeList &includes) + { + m_extraIncludes = includes; + } + void addExtraInclude(const Include &include) + { + if (!m_includesUsed.value(include.name, false)) { + m_extraIncludes << include; + m_includesUsed[include.name] = true; + } + } + + ComplexTypeEntry *copy() const + { + ComplexTypeEntry *centry = new ComplexTypeEntry(name(), type()); + centry->setInclude(include()); + centry->setExtraIncludes(extraIncludes()); + centry->setFunctionModifications(functionModifications()); + centry->setFieldModifications(fieldModifications()); + centry->setQObject(isQObject()); + centry->setDefaultSuperclass(defaultSuperclass()); + centry->setCodeSnips(codeSnips()); + centry->setTargetLangPackage(targetLangPackage()); + + return centry; + } + + void setLookupName(const QString &name) + { + m_lookupName = name; + } + + virtual QString lookupName() const + { + return m_lookupName.isEmpty() ? targetLangName() : m_lookupName; + } + + QString targetLangApiName() const + { + return strings_jobject; + } + + + Include include() const + { + return m_include; + } + void setInclude(const Include &inc) + { + m_include = inc; + } + + void setTypeFlags(TypeFlags flags) + { + m_typeFlags = flags; + } + + TypeFlags typeFlags() const + { + return m_typeFlags; + } + + FunctionModificationList functionModifications() const + { + return m_functionMods; + } + void setFunctionModifications(const FunctionModificationList &functionModifications) + { + m_functionMods = functionModifications; + } + void addFunctionModification(const FunctionModification &functionModification) + { + m_functionMods << functionModification; + } + FunctionModificationList functionModifications(const QString &signature) const; + + FieldModification fieldModification(const QString &name) const; + void setFieldModifications(const FieldModificationList &mods) + { + m_fieldMods = mods; + } + FieldModificationList fieldModifications() const + { + return m_fieldMods; + } + + QString targetLangPackage() const + { + return m_package; + } + void setTargetLangPackage(const QString &package) + { + m_package = package; + } + + bool isQObject() const + { + return m_qobject; + } + void setQObject(bool qobject) + { + m_qobject = qobject; + } + + QString defaultSuperclass() const + { + return m_defaultSuperclass; + } + void setDefaultSuperclass(const QString &sc) + { + m_defaultSuperclass = sc; + } + + virtual QString qualifiedCppName() const + { + return m_qualifiedCppName; + } + + + void setIsPolymorphicBase(bool on) + { + m_polymorphicBase = on; + } + bool isPolymorphicBase() const + { + return m_polymorphicBase; + } + + void setPolymorphicIdValue(const QString &value) + { + m_polymorphicIdValue = value; + } + QString polymorphicIdValue() const + { + return m_polymorphicIdValue; + } + + void setHeldType(const QString &value) + { + m_heldTypeValue = value; + } + QString heldTypeValue() const + { + return m_heldTypeValue; + } + + + void setExpensePolicy(const ExpensePolicy &policy) + { + m_expensePolicy = policy; + } + const ExpensePolicy &expensePolicy() const + { + return m_expensePolicy; + } + + QString targetType() const + { + return m_targetType; + } + void setTargetType(const QString &code) + { + m_targetType = code; + } + + QString targetLangName() const + { + return m_targetLangName.isEmpty() + ? TypeEntry::targetLangName() + : m_targetLangName; + } + void setTargetLangName(const QString &name) + { + m_targetLangName = name; + } + + bool isGenericClass() const + { + return m_genericClass; + } + void setGenericClass(bool isGeneric) + { + m_genericClass = isGeneric; + } + + CopyableFlag copyable() const + { + return m_copyableFlag; + } + void setCopyable(CopyableFlag flag) + { + m_copyableFlag = flag; + } + + QString hashFunction() const + { + return m_hashFunction; + } + void setHashFunction(QString hashFunction) + { + m_hashFunction = hashFunction; + } + + +private: + IncludeList m_extraIncludes; + Include m_include; + QHash m_includesUsed; + FunctionModificationList m_functionMods; + FieldModificationList m_fieldMods; + QString m_package; + QString m_defaultSuperclass; + QString m_qualifiedCppName; + QString m_targetLangName; + + uint m_qobject : 1; + uint m_polymorphicBase : 1; + uint m_genericClass : 1; + + QString m_polymorphicIdValue; + QString m_heldTypeValue; + QString m_lookupName; + QString m_targetType; + ExpensePolicy m_expensePolicy; + TypeFlags m_typeFlags; + CopyableFlag m_copyableFlag; + QString m_hashFunction; +}; + +class ContainerTypeEntry : public ComplexTypeEntry +{ +public: + enum Type { + NoContainer, + ListContainer, + StringListContainer, + LinkedListContainer, + VectorContainer, + StackContainer, + QueueContainer, + SetContainer, + MapContainer, + MultiMapContainer, + HashContainer, + MultiHashContainer, + PairContainer, + }; + + ContainerTypeEntry(const QString &name, Type type) + : ComplexTypeEntry(name, ContainerType), m_type(type) + { + setCodeGeneration(GenerateForSubclass); + } + + Type type() const + { + return m_type; + } + QString targetLangName() const; + QString targetLangPackage() const; + QString qualifiedCppName() const; + + static Type containerTypeFromString(QString typeName) + { + static QHash m_stringToContainerType; + if (m_stringToContainerType.isEmpty()) { + m_stringToContainerType["list"] = ListContainer; + m_stringToContainerType["string-list"] = StringListContainer; + m_stringToContainerType["linked-list"] = LinkedListContainer; + m_stringToContainerType["vector"] = VectorContainer; + m_stringToContainerType["stack"] = StackContainer; + m_stringToContainerType["queue"] = QueueContainer; + m_stringToContainerType["set"] = SetContainer; + m_stringToContainerType["map"] = MapContainer; + m_stringToContainerType["multi-map"] = MultiMapContainer; + m_stringToContainerType["hash"] = HashContainer; + m_stringToContainerType["multi-hash"] = MultiHashContainer; + m_stringToContainerType["pair"] = PairContainer; + } + return m_stringToContainerType.value(typeName, NoContainer); + } + +private: + Type m_type; +}; + + +class NamespaceTypeEntry : public ComplexTypeEntry +{ +public: + NamespaceTypeEntry(const QString &name) : ComplexTypeEntry(name, NamespaceType) { } +}; + + +class ValueTypeEntry : public ComplexTypeEntry +{ +public: + ValueTypeEntry(const QString &name) : ComplexTypeEntry(name, BasicValueType) { } + + bool isValue() const + { + return true; + } + + virtual bool isNativeIdBased() const + { + return true; + } + +protected: + ValueTypeEntry(const QString &name, Type t) : ComplexTypeEntry(name, t) { } +}; + + +class StringTypeEntry : public ValueTypeEntry +{ +public: + StringTypeEntry(const QString &name) + : ValueTypeEntry(name, StringType) + { + setCodeGeneration(GenerateNothing); + } + + QString targetLangApiName() const + { + return strings_jobject; + } + QString targetLangName() const + { + return strings_String; + } + QString targetLangPackage() const + { + return stringsJavaLang; + } + + virtual bool isNativeIdBased() const + { + return false; + } +}; + +class CharTypeEntry : public ValueTypeEntry +{ +public: + CharTypeEntry(const QString &name) : ValueTypeEntry(name, CharType) + { + setCodeGeneration(GenerateNothing); + } + + QString targetLangApiName() const + { + return strings_jchar; + } + QString targetLangName() const + { + return strings_char; + } + QString targetLangPackage() const + { + return QString(); + } + + virtual bool isNativeIdBased() const + { + return false; + } +}; + +class JObjectWrapperTypeEntry: public ValueTypeEntry +{ +public: + JObjectWrapperTypeEntry(const QString &name) : ValueTypeEntry(name, JObjectWrapperType) { } + + QString targetLangApiName() const + { + return strings_jobject; + } + QString targetLangName() const + { + return strings_Object; + } + QString targetLangPackage() const + { + return stringsJavaLang; + } + + bool isNativeIdBased() const + { + return false; + } +}; + +class VariantTypeEntry: public ValueTypeEntry +{ +public: + VariantTypeEntry(const QString &name) : ValueTypeEntry(name, VariantType) { } + + QString targetLangApiName() const + { + return strings_jobject; + } + QString targetLangName() const + { + return strings_Object; + } + QString targetLangPackage() const + { + return stringsJavaLang; + } + + virtual bool isNativeIdBased() const + { + return false; + } +}; + + +class InterfaceTypeEntry : public ComplexTypeEntry +{ +public: + InterfaceTypeEntry(const QString &name) + : ComplexTypeEntry(name, InterfaceType) {} + + static QString interfaceName(const QString &name) + { + return name + "Interface"; + } + + ObjectTypeEntry *origin() const + { + return m_origin; + } + void setOrigin(ObjectTypeEntry *origin) + { + m_origin = origin; + } + + virtual bool isNativeIdBased() const + { + return true; + } + virtual QString qualifiedCppName() const + { + return ComplexTypeEntry::qualifiedCppName().left(ComplexTypeEntry::qualifiedCppName().length() - interfaceName("").length()); + } + +private: + ObjectTypeEntry *m_origin; +}; + + +class ObjectTypeEntry : public ComplexTypeEntry +{ +public: + ObjectTypeEntry(const QString &name) + : ComplexTypeEntry(name, ObjectType), m_interface(0) {} + + InterfaceTypeEntry *designatedInterface() const + { + return m_interface; + } + void setDesignatedInterface(InterfaceTypeEntry *entry) + { + m_interface = entry; + } + + virtual bool isNativeIdBased() const + { + return true; + } + +private: + InterfaceTypeEntry *m_interface; +}; + +class CustomTypeEntry : public ComplexTypeEntry +{ +public: + CustomTypeEntry(const QString &name) : ComplexTypeEntry(name, CustomType) {} + + virtual void generateCppTargetLangToQt(QTextStream &s, + const AbstractMetaType* targetLangType, + const QString &envName, + const QString &qtName, + const QString &targetLangName) const = 0; + + virtual void generateCppQtToTargetLang(QTextStream &s, + const AbstractMetaType* targetLangType, + const QString &envName, + const QString &qtName, + const QString &targetLangName) const = 0; +}; + +struct TypeRejection +{ + QString class_name; + QString function_name; + QString field_name; + QString enum_name; +}; + +class TypeDatabase +{ +public: + TypeDatabase(); + + static TypeDatabase *instance(); + + QStringList requiredTargetImports() + { + return m_requiredTargetImports; + } + + void addRequiredTargetImport(const QString &moduleName) + { + if (!m_requiredTargetImports.contains(moduleName)) + m_requiredTargetImports << moduleName; + } + + QStringList typesystemPaths() + { + return m_typesystemPaths; + } + void addTypesystemPath(const QString &typesystem_paths) + { +#if defined(Q_OS_WIN32) + char *path_splitter = const_cast(";"); +#else + char *path_splitter = const_cast(":"); +#endif + m_typesystemPaths += typesystem_paths.split(path_splitter); + } + + IncludeList extraIncludes(const QString &className); + + inline PrimitiveTypeEntry *findPrimitiveType(const QString &name); + inline ComplexTypeEntry *findComplexType(const QString &name); + inline ObjectTypeEntry *findObjectType(const QString &name); + inline NamespaceTypeEntry *findNamespaceType(const QString &name); + ContainerTypeEntry *findContainerType(const QString &name); + + TypeEntry *findType(const QString &name) const + { + QList entries = findTypes(name); + foreach (TypeEntry *entry, entries) { + if (entry && + (!entry->isPrimitive() || static_cast(entry)->preferredTargetLangType())) { + return entry; + } + } + return 0; + } + QList findTypes(const QString &name) const + { + return m_entries.value(name); + } + TypeEntryHash allEntries() + { + return m_entries; + } + + SingleTypeEntryHash entries() + { + TypeEntryHash entries = allEntries(); + + SingleTypeEntryHash returned; + QList keys = entries.keys(); + + foreach (QString key, keys) + returned[key] = findType(key); + + return returned; + } + + PrimitiveTypeEntry *findTargetLangPrimitiveType(const QString &targetLangName); + + QList primitiveTypes() { + TypeEntryHash entries = allEntries(); + QList returned; + foreach(QString key, entries.keys()) { + foreach(const TypeEntry* typeEntry, entries[key]) { + if (typeEntry->isPrimitive()) + returned.append((PrimitiveTypeEntry*) typeEntry); + } + } + return returned; + } + QList containerTypes() { + TypeEntryHash entries = allEntries(); + QList returned; + foreach(QString key, entries.keys()) { + foreach(const TypeEntry* typeEntry, entries[key]) { + if (typeEntry->isContainer()) + returned.append((ContainerTypeEntry*) typeEntry); + } + } + return returned; + } + + void addRejection(const QString &class_name, const QString &function_name, + const QString &field_name, const QString &enum_name); + bool isClassRejected(const QString &class_name); + bool isFunctionRejected(const QString &class_name, const QString &function_name); + bool isFieldRejected(const QString &class_name, const QString &field_name); + bool isEnumRejected(const QString &class_name, const QString &enum_name); + + void addType(TypeEntry *e) + { + m_entries[e->qualifiedCppName()].append(e); + } + + SingleTypeEntryHash flagsEntries() const + { + return m_flagsEntries; + } + FlagsTypeEntry *findFlagsType(const QString &name) const; + void addFlagsType(FlagsTypeEntry *fte) + { + m_flagsEntries[fte->originalName()] = fte; + } + + TemplateEntry *findTemplate(const QString &name) + { + return m_templates[name]; + } + void addTemplate(TemplateEntry *t) + { + m_templates[t->name()] = t; + } + + void setSuppressWarnings(bool on) + { + m_suppressWarnings = on; + } + void addSuppressedWarning(const QString &s) + { + m_suppressedWarnings.append(s); + } + + bool isSuppressedWarning(const QString &s) + { + if (!m_suppressWarnings) + return false; + + foreach (const QString &_warning, m_suppressedWarnings) { + QString warning(QString(_warning).replace("\\*", "&place_holder_for_asterisk;")); + + QStringList segs = warning.split("*", QString::SkipEmptyParts); + if (!segs.size()) + continue; + + int i = 0; + int pos = s.indexOf(QString(segs.at(i++)).replace("&place_holder_for_asterisk;", "*")); + //qDebug() << "s == " << s << ", warning == " << segs; + while (pos != -1) { + if (i == segs.size()) + return true; + pos = s.indexOf(QString(segs.at(i++)).replace("&place_holder_for_asterisk;", "*"), pos); + } + } + + return false; + } + + void setRebuildClasses(const QStringList &cls) + { + m_rebuildClasses = cls; + } + + static QString globalNamespaceClassName(const TypeEntry *te); + QString filename() const + { + return "typesystem.txt"; + } + + QString modifiedTypesystemFilepath(const QString &ts_file); + bool parseFile(const QString &filename, bool generate = true); + bool parseFile(QIODevice* device, bool generate = true); + +private: + bool m_suppressWarnings; + TypeEntryHash m_entries; + SingleTypeEntryHash m_flagsEntries; + TemplateEntryHash m_templates; + QStringList m_suppressedWarnings; + + QStringList m_requiredTargetImports; + + QStringList m_typesystemPaths; + QHash m_parsedTypesystemFiles; + + QList m_rejections; + QStringList m_rebuildClasses; +}; + +inline PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString &name) +{ + QList entries = findTypes(name); + + foreach (TypeEntry *entry, entries) { + if (entry && entry->isPrimitive() && static_cast(entry)->preferredTargetLangType()) + return static_cast(entry); + } + + return 0; +} + +inline ComplexTypeEntry *TypeDatabase::findComplexType(const QString &name) +{ + TypeEntry *entry = findType(name); + if (entry && entry->isComplex()) + return static_cast(entry); + else + return 0; +} + +inline ObjectTypeEntry *TypeDatabase::findObjectType(const QString &name) +{ + TypeEntry *entry = findType(name); + if (entry && entry->isObject()) + return static_cast(entry); + else + return 0; +} + +inline NamespaceTypeEntry *TypeDatabase::findNamespaceType(const QString &name) +{ + TypeEntry *entry = findType(name); + if (entry && entry->isNamespace()) + return static_cast(entry); + else + return 0; +} + +QString fixCppTypeName(const QString &name); + +#endif // TYPESYSTEM_H -- cgit v1.2.3