├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── codequality ├── HEADER └── checkstyle.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── install ├── settings.gradle └── src ├── main ├── ghpages │ └── index.html ├── java │ └── org │ │ └── anarres │ │ └── cpp │ │ ├── Argument.java │ │ ├── BuildMetadata.java │ │ ├── ChrootFileSystem.java │ │ ├── CppReader.java │ │ ├── CppTask.java │ │ ├── DefaultPreprocessorListener.java │ │ ├── Feature.java │ │ ├── FileLexerSource.java │ │ ├── FixedTokenSource.java │ │ ├── InputLexerSource.java │ │ ├── InternalException.java │ │ ├── JavaFileSystem.java │ │ ├── JoinReader.java │ │ ├── LexerException.java │ │ ├── LexerSource.java │ │ ├── Macro.java │ │ ├── MacroTokenSource.java │ │ ├── Main.java │ │ ├── NumericValue.java │ │ ├── Preprocessor.java │ │ ├── PreprocessorCommand.java │ │ ├── PreprocessorListener.java │ │ ├── ResourceFileSystem.java │ │ ├── Source.java │ │ ├── SourceIterator.java │ │ ├── State.java │ │ ├── StringLexerSource.java │ │ ├── Token.java │ │ ├── TokenSnifferSource.java │ │ ├── TokenType.java │ │ ├── VirtualFile.java │ │ ├── VirtualFileSystem.java │ │ └── Warning.java └── velocity │ └── org │ └── anarres │ └── cpp │ └── Version.java ├── scripts ├── jcpp └── release.sh └── test ├── java └── org │ └── anarres │ └── cpp │ ├── BuildMetadataTest.java │ ├── CppReaderTest.java │ ├── ErrorTest.java │ ├── IncludeAbsoluteTest.java │ ├── JavaFileSystemTest.java │ ├── JoinReaderTest.java │ ├── LexerSourceTest.java │ ├── MainTest.java │ ├── NumericValueTest.java │ ├── PragmaTest.java │ ├── PreprocessorTest.java │ ├── RegressionTest.java │ ├── TokenPastingWhitespaceTest.java │ └── VaArgsPastingTest.java └── resources ├── absolute.h ├── lines.c ├── lines1.h ├── lines2.h ├── once.c ├── once.h ├── pragma.c ├── regression └── lex-char.in ├── test0.c ├── test0.h ├── test1.c ├── test1.h ├── trigraph.c └── varargs.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.rar 19 | *.tar 20 | *.zip 21 | 22 | # Logs and databases # 23 | ###################### 24 | *.log 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store* 29 | ehthumbs.db 30 | Icon? 31 | Thumbs.db 32 | 33 | # Editor Files # 34 | ################ 35 | *~ 36 | *.swp 37 | 38 | # Gradle Files # 39 | ################ 40 | .gradle 41 | 42 | # Build output directies 43 | /target 44 | */target 45 | /build 46 | */build 47 | 48 | # IntelliJ specific files/directories 49 | out 50 | .idea 51 | *.ipr 52 | *.iws 53 | *.iml 54 | atlassian-ide-plugin.xml 55 | 56 | # Eclipse specific files/directories 57 | .classpath 58 | .project 59 | .settings 60 | .metadata 61 | 62 | # NetBeans specific files/directories 63 | .nbattrs 64 | .nb-gradle 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | The C Preprocessor is an interesting standard. It appears to be 4 | derived from the de-facto behaviour of the first preprocessors, and 5 | has evolved over the years. Implementation is therefore difficult. 6 | 7 | JCPP is a complete, compliant, standalone, pure Java implementation 8 | of the C preprocessor. It is intended to be of use to people writing 9 | C-style compilers in Java using tools like sablecc, antlr, JLex, 10 | CUP and so forth (although if you aren't using sablecc, you need your 11 | head examined). 12 | 13 | This project has has been used to successfully preprocess much of 14 | the source code of the GNU C library. As of version 1.2.5, it can 15 | also preprocess the Apple Objective C library. 16 | 17 | # Documentation 18 | 19 | * [JavaDoc API](http://shevek.github.io/jcpp/docs/javadoc/) 20 | * [Coverage Report](http://shevek.github.io/jcpp/docs/cobertura/) 21 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | // mavenLocal() 4 | mavenCentral() 5 | jcenter() 6 | // maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } 7 | } 8 | 9 | dependencies { 10 | classpath 'org.anarres.gradle:gradle-stdproject-plugin:1.0.9' 11 | } 12 | } 13 | 14 | apply plugin: 'org.anarres.stdproject' 15 | stdproject { 16 | javadocLinkSource = true; 17 | } 18 | 19 | group = "org.anarres" 20 | 21 | apply plugin: 'org.anarres.stdmodule' 22 | stdmodule { 23 | description "An embeddable C Preprocessor for the JVM." 24 | author id: 'shevek', name: 'Shevek', email: 'github@anarres.org' 25 | license 'Apache-2.0' 26 | } 27 | 28 | sourceCompatibility = 1.5 29 | 30 | dependencies { 31 | compile 'com.google.code.findbugs:annotations:3.0.1' 32 | compile 'org.slf4j:slf4j-api:1.7.12' 33 | 34 | compile 'net.sf.jopt-simple:jopt-simple:4.7' 35 | compile 'org.apache.ant:ant:1.7.0' 36 | compile 'com.github.zafarkhaja:java-semver:0.8.0' 37 | 38 | testCompile 'com.google.guava:guava:18.0' 39 | } 40 | 41 | // This ensures that the info-plugin's properties file is in the 42 | // same location for the test suite as in the JAR. 43 | task('processTestVersionResources', type: Copy, dependsOn: processTestResources) { 44 | into project.sourceSets.test.output.resourcesDir 45 | from(writeManifestProperties) { 46 | into "META-INF" 47 | } 48 | } 49 | testClasses.dependsOn(processTestVersionResources) 50 | 51 | apply plugin: 'application' 52 | 53 | mainClassName = "org.anarres.cpp.Main" 54 | -------------------------------------------------------------------------------- /codequality/HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${year} Shevek. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /codequality/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.4.15-SNAPSHOT 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevek/jcpp/5e50e75ec33f5b4567cabfd60b6baca39524a8b7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | build/install/jcpp/ -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='jcpp' 2 | -------------------------------------------------------------------------------- /src/main/ghpages/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Javadoc 4 | Coverage 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Argument.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import javax.annotation.Nonnull; 24 | 25 | /** 26 | * A macro argument. 27 | * 28 | * This encapsulates a raw and preprocessed token stream. 29 | */ 30 | /* pp */ class Argument extends ArrayList { 31 | 32 | private List expansion; 33 | 34 | public Argument() { 35 | this.expansion = null; 36 | } 37 | 38 | public void addToken(@Nonnull Token tok) { 39 | add(tok); 40 | } 41 | 42 | /* pp */ void expand(@Nonnull Preprocessor p) 43 | throws IOException, 44 | LexerException { 45 | /* Cache expansion. */ 46 | if (expansion == null) { 47 | this.expansion = p.expand(this); 48 | // System.out.println("Expanded arg " + this); 49 | } 50 | } 51 | 52 | @Nonnull 53 | public Iterator expansion() { 54 | return expansion.iterator(); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | StringBuilder buf = new StringBuilder(); 60 | buf.append("Argument("); 61 | // buf.append(super.toString()); 62 | buf.append("raw=[ "); 63 | for (int i = 0; i < size(); i++) 64 | buf.append(get(i).getText()); 65 | buf.append(" ];expansion=[ "); 66 | if (expansion == null) 67 | buf.append("null"); 68 | else 69 | for (Token token : expansion) 70 | buf.append(token.getText()); 71 | buf.append(" ])"); 72 | return buf.toString(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/BuildMetadata.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.URL; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.Properties; 12 | import javax.annotation.Nonnull; 13 | 14 | /** 15 | * Returns information about the build. 16 | * 17 | * @author shevek 18 | */ 19 | public class BuildMetadata { 20 | 21 | public static final String RESOURCE = "/META-INF/jcpp.properties"; 22 | private static BuildMetadata INSTANCE; 23 | 24 | /** @throws RuntimeException if the properties file cannot be found on the classpath. */ 25 | @Nonnull 26 | public static synchronized BuildMetadata getInstance() { 27 | try { 28 | if (INSTANCE == null) 29 | INSTANCE = new BuildMetadata(); 30 | return INSTANCE; 31 | } catch (IOException e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | private final Properties properties = new Properties(); 37 | 38 | private BuildMetadata() throws IOException { 39 | URL url = BuildMetadata.class.getResource(RESOURCE); 40 | InputStream in = url.openStream(); 41 | try { 42 | properties.load(in); 43 | } finally { 44 | in.close(); 45 | } 46 | } 47 | 48 | @Nonnull 49 | public Map asMap() { 50 | Map out = new HashMap(); 51 | for (Map.Entry e : properties.entrySet()) 52 | out.put(String.valueOf(e.getKey()), String.valueOf(e.getValue())); 53 | return out; 54 | } 55 | 56 | @Nonnull 57 | public com.github.zafarkhaja.semver.Version getVersion() { 58 | return com.github.zafarkhaja.semver.Version.valueOf(properties.getProperty("Implementation-Version")); 59 | } 60 | 61 | @Nonnull 62 | public Date getBuildDate() throws ParseException { 63 | // Build-Date=2015-01-01_10:09:09 64 | String text = properties.getProperty("Build-Date"); 65 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); 66 | return format.parse(text); 67 | } 68 | 69 | public String getChangeId() { 70 | return properties.getProperty("Change"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/ChrootFileSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | 22 | /** 23 | * A virtual filesystem implementation using java.io in a virtual 24 | * chroot. 25 | */ 26 | public class ChrootFileSystem implements VirtualFileSystem { 27 | 28 | private File root; 29 | 30 | public ChrootFileSystem(File root) { 31 | this.root = root; 32 | } 33 | 34 | @Override 35 | public VirtualFile getFile(String path) { 36 | return new ChrootFile(path); 37 | } 38 | 39 | @Override 40 | public VirtualFile getFile(String dir, String name) { 41 | return new ChrootFile(dir, name); 42 | } 43 | 44 | private class ChrootFile extends File implements VirtualFile { 45 | 46 | private File rfile; 47 | 48 | public ChrootFile(String path) { 49 | super(path); 50 | } 51 | 52 | public ChrootFile(String dir, String name) { 53 | super(dir, name); 54 | } 55 | 56 | /* private */ 57 | public ChrootFile(File dir, String name) { 58 | super(dir, name); 59 | } 60 | 61 | @Override 62 | public ChrootFile getParentFile() { 63 | return new ChrootFile(getParent()); 64 | } 65 | 66 | @Override 67 | public ChrootFile getChildFile(String name) { 68 | return new ChrootFile(this, name); 69 | } 70 | 71 | @Override 72 | public boolean isFile() { 73 | File real = new File(root, getPath()); 74 | return real.isFile(); 75 | } 76 | 77 | @Override 78 | public Source getSource() throws IOException { 79 | return new FileLexerSource(new File(root, getPath()), 80 | getPath()); 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/CppReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.Closeable; 20 | import java.io.IOException; 21 | import java.io.Reader; 22 | import javax.annotation.Nonnull; 23 | import static org.anarres.cpp.Token.CCOMMENT; 24 | import static org.anarres.cpp.Token.CPPCOMMENT; 25 | import static org.anarres.cpp.Token.EOF; 26 | 27 | /** 28 | * A Reader wrapper around the Preprocessor. 29 | * 30 | * This is a utility class to provide a transparent {@link Reader} 31 | * which preprocesses the input text. 32 | * 33 | * @see Preprocessor 34 | * @see Reader 35 | */ 36 | public class CppReader extends Reader implements Closeable { 37 | 38 | private final Preprocessor cpp; 39 | private String token; 40 | private int idx; 41 | 42 | public CppReader(@Nonnull final Reader r) { 43 | cpp = new Preprocessor(new LexerSource(r, true) { 44 | @Override 45 | public String getName() { 46 | return ""; 48 | } 49 | }); 50 | token = ""; 51 | idx = 0; 52 | } 53 | 54 | public CppReader(@Nonnull Preprocessor p) { 55 | cpp = p; 56 | token = ""; 57 | idx = 0; 58 | } 59 | 60 | /** 61 | * Returns the Preprocessor used by this CppReader. 62 | */ 63 | @Nonnull 64 | public Preprocessor getPreprocessor() { 65 | return cpp; 66 | } 67 | 68 | /** 69 | * Defines the given name as a macro. 70 | * 71 | * This is a convnience method. 72 | */ 73 | public void addMacro(@Nonnull String name) 74 | throws LexerException { 75 | cpp.addMacro(name); 76 | } 77 | 78 | /** 79 | * Defines the given name as a macro. 80 | * 81 | * This is a convnience method. 82 | */ 83 | public void addMacro(@Nonnull String name, @Nonnull String value) 84 | throws LexerException { 85 | cpp.addMacro(name, value); 86 | } 87 | 88 | private boolean refill() 89 | throws IOException { 90 | try { 91 | assert cpp != null : "cpp is null : was it closed?"; 92 | if (token == null) 93 | return false; 94 | while (idx >= token.length()) { 95 | Token tok = cpp.token(); 96 | switch (tok.getType()) { 97 | case EOF: 98 | token = null; 99 | return false; 100 | case CCOMMENT: 101 | case CPPCOMMENT: 102 | if (!cpp.getFeature(Feature.KEEPCOMMENTS)) { 103 | token = " "; 104 | break; 105 | } 106 | default: 107 | token = tok.getText(); 108 | break; 109 | } 110 | idx = 0; 111 | } 112 | return true; 113 | } catch (LexerException e) { 114 | // new IOException(String, Throwable) is since 1.6 115 | IOException _e = new IOException(String.valueOf(e)); 116 | _e.initCause(e); 117 | throw _e; 118 | } 119 | } 120 | 121 | @Override 122 | public int read() 123 | throws IOException { 124 | if (!refill()) 125 | return -1; 126 | return token.charAt(idx++); 127 | } 128 | 129 | @Override 130 | /* XXX Very slow and inefficient. */ 131 | public int read(char cbuf[], int off, int len) 132 | throws IOException { 133 | if (token == null) 134 | return -1; 135 | for (int i = 0; i < len; i++) { 136 | int ch = read(); 137 | if (ch == -1) 138 | return i; 139 | cbuf[off + i] = (char) ch; 140 | } 141 | return len; 142 | } 143 | 144 | @Override 145 | public void close() 146 | throws IOException { 147 | cpp.close(); 148 | token = null; 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/CppTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.File; 20 | import java.io.FileWriter; 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.Enumeration; 25 | import java.util.List; 26 | import org.apache.tools.ant.BuildException; 27 | import org.apache.tools.ant.taskdefs.Copy; 28 | import org.apache.tools.ant.types.Path; 29 | 30 | /** 31 | * An ant task for jcpp. 32 | */ 33 | public class CppTask extends Copy { 34 | 35 | private class Listener extends DefaultPreprocessorListener { 36 | 37 | @Override 38 | protected void print(String msg) { 39 | log(msg); 40 | } 41 | } 42 | 43 | public static class Macro { 44 | 45 | private String name; 46 | private String value; 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public String getName() { 53 | return name; 54 | } 55 | 56 | public void setValue(String value) { 57 | this.value = value; 58 | } 59 | 60 | public String getValue() { 61 | return value; 62 | } 63 | } 64 | 65 | private final Listener listener = new Listener(); 66 | private final List macros = new ArrayList(); 67 | private Path systemincludepath; 68 | private Path localincludepath; 69 | 70 | public void addMacro(Macro macro) { 71 | macros.add(macro); 72 | } 73 | 74 | public void addSystemincludepath(Path path) { 75 | if (systemincludepath == null) 76 | systemincludepath = new Path(getProject()); 77 | systemincludepath.add(path); 78 | } 79 | 80 | public void addLocalincludepath(Path path) { 81 | if (localincludepath == null) 82 | localincludepath = new Path(getProject()); 83 | localincludepath.add(path); 84 | } 85 | 86 | /* 87 | public void execute() { 88 | FileWriter writer = null; 89 | try { 90 | if (input == null) 91 | throw new BuildException("Input not specified"); 92 | if (output == null) 93 | throw new BuildException("Output not specified"); 94 | cpp.addInput(this.input); 95 | writer = new FileWriter(this.output); 96 | for (;;) { 97 | Token tok = cpp.token(); 98 | if (tok != null && tok.getType() == Token.EOF) 99 | break; 100 | writer.write(tok.getText()); 101 | } 102 | } 103 | catch (Exception e) { 104 | throw new BuildException(e); 105 | } 106 | finally { 107 | if (writer != null) { 108 | try { 109 | writer.close(); 110 | } 111 | catch (IOException e) { 112 | } 113 | } 114 | } 115 | } 116 | */ 117 | private void preprocess(File input, File output) throws Exception { 118 | if (input == null) 119 | throw new BuildException("Input not specified"); 120 | if (output == null) 121 | throw new BuildException("Output not specified"); 122 | 123 | Preprocessor cpp = new Preprocessor(); 124 | cpp.setListener(listener); 125 | for (Macro macro : macros) 126 | cpp.addMacro(macro.getName(), macro.getValue()); 127 | if (systemincludepath != null) 128 | cpp.setSystemIncludePath(Arrays.asList(systemincludepath.list())); 129 | if (localincludepath != null) 130 | cpp.setQuoteIncludePath(Arrays.asList(localincludepath.list())); 131 | 132 | File dir = output.getParentFile(); 133 | if (!dir.exists()) { 134 | if (!dir.mkdirs()) 135 | throw new BuildException("Failed to make parent directory " + dir); 136 | } else if (!dir.isDirectory()) { 137 | throw new BuildException("Parent directory of output file " + output + " exists, but is not a directory."); 138 | } 139 | FileWriter writer = null; 140 | try { 141 | cpp.addInput(input); 142 | writer = new FileWriter(output); 143 | for (;;) { 144 | Token tok = cpp.token(); 145 | if (tok == null) 146 | break; 147 | if (tok.getType() == Token.EOF) 148 | break; 149 | writer.write(tok.getText()); 150 | } 151 | } finally { 152 | if (writer != null) { 153 | try { 154 | writer.close(); 155 | } catch (IOException e) { 156 | } 157 | } 158 | } 159 | } 160 | 161 | @Override 162 | protected void doFileOperations() { 163 | if (fileCopyMap.size() > 0) { 164 | log("Copying " + fileCopyMap.size() 165 | + " file" + (fileCopyMap.size() == 1 ? "" : "s") 166 | + " to " + destDir.getAbsolutePath()); 167 | 168 | Enumeration e = fileCopyMap.keys(); 169 | 170 | while (e.hasMoreElements()) { 171 | String fromFile = e.nextElement(); 172 | String[] toFiles = (String[]) fileCopyMap.get(fromFile); 173 | 174 | for (String toFile : toFiles) { 175 | if (fromFile.equals(toFile)) { 176 | log("Skipping self-copy of " + fromFile, verbosity); 177 | continue; 178 | } 179 | 180 | try { 181 | log("Copying " + fromFile + " to " + toFile, verbosity); 182 | 183 | /* 184 | FilterSetCollection executionFilters 185 | = new FilterSetCollection(); 186 | if (filtering) { 187 | executionFilters 188 | .addFilterSet(getProject().getGlobalFilterSet()); 189 | } 190 | for (Enumeration filterEnum = getFilterSets().elements(); 191 | filterEnum.hasMoreElements();) { 192 | executionFilters 193 | .addFilterSet((FilterSet) filterEnum.nextElement()); 194 | } 195 | */ 196 | File srcFile = new File(fromFile); 197 | File dstFile = new File(toFile); 198 | preprocess(srcFile, dstFile); 199 | } catch (Exception ioe) { 200 | // ioe.printStackTrace(); 201 | String msg = "Failed to copy " + fromFile + " to " + toFile 202 | + " due to " + ioe.getMessage(); 203 | File targetFile = new File(toFile); 204 | if (targetFile.exists() && !targetFile.delete()) { 205 | msg += " and I couldn't delete the corrupt " + toFile; 206 | } 207 | throw new BuildException(msg, ioe, getLocation()); 208 | } 209 | } 210 | } 211 | } 212 | 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | /* 4 | * Anarres C Preprocessor 5 | * Copyright (c) 2007-2015, Shevek 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 16 | * or implied. See the License for the specific language governing 17 | * permissions and limitations under the License. 18 | */ 19 | import javax.annotation.Nonnegative; 20 | import javax.annotation.Nonnull; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | /** 25 | * A handler for preprocessor events, primarily errors and warnings. 26 | * 27 | * If no PreprocessorListener is installed in a Preprocessor, all 28 | * error and warning events will throw an exception. Installing a 29 | * listener allows more intelligent handling of these events. 30 | */ 31 | public class DefaultPreprocessorListener implements PreprocessorListener { 32 | 33 | private static final Logger LOG = LoggerFactory.getLogger(DefaultPreprocessorListener.class); 34 | 35 | private int errors; 36 | private int warnings; 37 | 38 | public DefaultPreprocessorListener() { 39 | clear(); 40 | } 41 | 42 | public void clear() { 43 | errors = 0; 44 | warnings = 0; 45 | } 46 | 47 | @Nonnegative 48 | public int getErrors() { 49 | return errors; 50 | } 51 | 52 | @Nonnegative 53 | public int getWarnings() { 54 | return warnings; 55 | } 56 | 57 | protected void print(@Nonnull String msg) { 58 | LOG.info(msg); 59 | } 60 | 61 | /** 62 | * Handles a warning. 63 | * 64 | * The behaviour of this method is defined by the 65 | * implementation. It may simply record the error message, or 66 | * it may throw an exception. 67 | */ 68 | @Override 69 | public void handleWarning(Source source, int line, int column, 70 | String msg) 71 | throws LexerException { 72 | warnings++; 73 | print(source.getName() + ":" + line + ":" + column 74 | + ": warning: " + msg); 75 | } 76 | 77 | /** 78 | * Handles an error. 79 | * 80 | * The behaviour of this method is defined by the 81 | * implementation. It may simply record the error message, or 82 | * it may throw an exception. 83 | */ 84 | @Override 85 | public void handleError(Source source, int line, int column, 86 | String msg) 87 | throws LexerException { 88 | errors++; 89 | print(source.getName() + ":" + line + ":" + column 90 | + ": error: " + msg); 91 | } 92 | 93 | @Override 94 | public void handleSourceChange(Source source, SourceChangeEvent event) { 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Feature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | /** 20 | * Features of the Preprocessor, which may be enabled or disabled. 21 | */ 22 | public enum Feature { 23 | 24 | /** Supports ANSI digraphs. */ 25 | DIGRAPHS, 26 | /** Supports ANSI trigraphs. */ 27 | TRIGRAPHS, 28 | /** Outputs linemarker tokens. */ 29 | LINEMARKERS, 30 | /** Reports tokens of type INVALID as errors. */ 31 | CSYNTAX, 32 | /** Preserves comments in the lexed output. Like cpp -C */ 33 | KEEPCOMMENTS, 34 | /** Preserves comments in the lexed output, even when inactive. */ 35 | KEEPALLCOMMENTS, 36 | DEBUG, 37 | /** Supports lexing of objective-C. */ 38 | OBJCSYNTAX, 39 | INCLUDENEXT, 40 | /** Random extensions. */ 41 | PRAGMA_ONCE 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/FileLexerSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.BufferedReader; 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileReader; 23 | import java.io.IOException; 24 | import java.nio.charset.Charset; 25 | import javax.annotation.Nonnull; 26 | 27 | /** 28 | * A {@link Source} which lexes a file. 29 | * 30 | * The input is buffered. 31 | * 32 | * @see Source 33 | */ 34 | public class FileLexerSource extends InputLexerSource { 35 | 36 | private final String path; 37 | private final File file; 38 | 39 | /** 40 | * Creates a new Source for lexing the given File. 41 | * 42 | * Preprocessor directives are honoured within the file. 43 | */ 44 | public FileLexerSource(@Nonnull File file, @Nonnull Charset charset, @Nonnull String path) 45 | throws IOException { 46 | super(new FileInputStream(file), charset); 47 | this.file = file; 48 | this.path = path; 49 | } 50 | 51 | public FileLexerSource(@Nonnull File file, @Nonnull String path) 52 | throws IOException { 53 | this(file, Charset.defaultCharset(), path); 54 | } 55 | 56 | public FileLexerSource(@Nonnull File file, @Nonnull Charset charset) 57 | throws IOException { 58 | this(file, charset, file.getPath()); 59 | } 60 | 61 | @Deprecated 62 | public FileLexerSource(@Nonnull File file) 63 | throws IOException { 64 | this(file, Charset.defaultCharset()); 65 | } 66 | 67 | public FileLexerSource(@Nonnull String path, @Nonnull Charset charset) 68 | throws IOException { 69 | this(new File(path), charset, path); 70 | } 71 | 72 | @Deprecated 73 | public FileLexerSource(@Nonnull String path) 74 | throws IOException { 75 | this(path, Charset.defaultCharset()); 76 | } 77 | 78 | @Nonnull 79 | public File getFile() { 80 | return file; 81 | } 82 | 83 | /** 84 | * This is not necessarily the same as getFile().getPath() in case we are in a chroot. 85 | */ 86 | @Override 87 | public String getPath() { 88 | return path; 89 | } 90 | 91 | @Override 92 | public String getName() { 93 | return getPath(); 94 | } 95 | 96 | @Override 97 | public String toString() { 98 | return "file " + getPath(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/FixedTokenSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.IOException; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | /* pp */ class FixedTokenSource extends Source { 24 | 25 | private static final Token EOF 26 | = new Token(Token.EOF, ""); 27 | 28 | private final List tokens; 29 | private int idx; 30 | 31 | /* pp */ FixedTokenSource(Token... tokens) { 32 | this.tokens = Arrays.asList(tokens); 33 | this.idx = 0; 34 | } 35 | 36 | /* pp */ FixedTokenSource(List tokens) { 37 | this.tokens = tokens; 38 | this.idx = 0; 39 | } 40 | 41 | @Override 42 | public Token token() 43 | throws IOException, 44 | LexerException { 45 | if (idx >= tokens.size()) 46 | return EOF; 47 | return tokens.get(idx++); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | StringBuilder buf = new StringBuilder(); 53 | buf.append("constant token stream ").append(tokens); 54 | Source parent = getParent(); 55 | if (parent != null) 56 | buf.append(" in ").append(String.valueOf(parent)); 57 | return buf.toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/InputLexerSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.InputStream; 20 | import java.io.InputStreamReader; 21 | import java.io.Reader; 22 | import java.nio.charset.Charset; 23 | import javax.annotation.Nonnull; 24 | 25 | /** 26 | * A {@link Source} which lexes an {@link InputStream}. 27 | * 28 | * The input is buffered. 29 | * 30 | * @see Source 31 | */ 32 | public class InputLexerSource extends LexerSource { 33 | 34 | @Deprecated 35 | public InputLexerSource(@Nonnull InputStream input) { 36 | this(input, Charset.defaultCharset()); 37 | } 38 | 39 | /** 40 | * Creates a new Source for lexing the given Reader. 41 | * 42 | * Preprocessor directives are honoured within the file. 43 | */ 44 | public InputLexerSource(@Nonnull InputStream input, Charset charset) { 45 | this(new InputStreamReader(input, charset)); 46 | } 47 | 48 | public InputLexerSource(@Nonnull Reader input, boolean ppvalid) { 49 | super(input, true); 50 | } 51 | 52 | public InputLexerSource(@Nonnull Reader input) { 53 | this(input, true); 54 | } 55 | 56 | @Override 57 | public String getPath() { 58 | return ""; 59 | } 60 | 61 | @Override 62 | public String getName() { 63 | return "standard input"; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return String.valueOf(getPath()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/InternalException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | /** 20 | * An internal exception. 21 | * 22 | * This exception is thrown when an internal state violation is 23 | * encountered. This should never happen. If it ever happens, please 24 | * report it as a bug. 25 | */ 26 | public class InternalException extends RuntimeException { 27 | 28 | public InternalException(String msg) { 29 | super(msg); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/JavaFileSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | 22 | /** 23 | * A virtual filesystem implementation using java.io. 24 | */ 25 | public class JavaFileSystem implements VirtualFileSystem { 26 | 27 | @Override 28 | public VirtualFile getFile(String path) { 29 | return new JavaFile(path); 30 | } 31 | 32 | @Override 33 | public VirtualFile getFile(String dir, String name) { 34 | return new JavaFile(dir, name); 35 | } 36 | 37 | private class JavaFile extends File implements VirtualFile { 38 | 39 | public JavaFile(String path) { 40 | super(path); 41 | } 42 | 43 | public JavaFile(String dir, String name) { 44 | super(dir, name); 45 | } 46 | 47 | /* private */ 48 | public JavaFile(File dir, String name) { 49 | super(dir, name); 50 | } 51 | 52 | /* 53 | @Override 54 | public String getPath() { 55 | return getCanonicalPath(); 56 | } 57 | */ 58 | @Override 59 | public JavaFile getParentFile() { 60 | String parent = getParent(); 61 | if (parent != null) 62 | return new JavaFile(parent); 63 | File absolute = getAbsoluteFile(); 64 | parent = absolute.getParent(); 65 | /* 66 | if (parent == null) 67 | return null; 68 | */ 69 | return new JavaFile(parent); 70 | } 71 | 72 | @Override 73 | public JavaFile getChildFile(String name) { 74 | return new JavaFile(this, name); 75 | } 76 | 77 | @Override 78 | public Source getSource() throws IOException { 79 | return new FileLexerSource(this); 80 | } 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/JoinReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.Closeable; 20 | import java.io.IOException; 21 | import java.io.Reader; 22 | 23 | /* pp */ class JoinReader /* extends Reader */ implements Closeable { 24 | 25 | private final Reader in; 26 | 27 | private PreprocessorListener listener; 28 | private LexerSource source; 29 | private boolean trigraphs; 30 | private boolean warnings; 31 | 32 | private int newlines; 33 | private boolean flushnl; 34 | private int[] unget; 35 | private int uptr; 36 | 37 | public JoinReader(Reader in, boolean trigraphs) { 38 | this.in = in; 39 | this.trigraphs = trigraphs; 40 | this.newlines = 0; 41 | this.flushnl = false; 42 | this.unget = new int[2]; 43 | this.uptr = 0; 44 | } 45 | 46 | public JoinReader(Reader in) { 47 | this(in, false); 48 | } 49 | 50 | public void setTrigraphs(boolean enable, boolean warnings) { 51 | this.trigraphs = enable; 52 | this.warnings = warnings; 53 | } 54 | 55 | /* pp */ void init(Preprocessor pp, LexerSource s) { 56 | this.listener = pp.getListener(); 57 | this.source = s; 58 | setTrigraphs(pp.getFeature(Feature.TRIGRAPHS), 59 | pp.getWarning(Warning.TRIGRAPHS)); 60 | } 61 | 62 | private int __read() throws IOException { 63 | if (uptr > 0) 64 | return unget[--uptr]; 65 | return in.read(); 66 | } 67 | 68 | private void _unread(int c) { 69 | if (c != -1) 70 | unget[uptr++] = c; 71 | assert uptr <= unget.length : 72 | "JoinReader ungets too many characters"; 73 | } 74 | 75 | protected void warning(String msg) 76 | throws LexerException { 77 | if (source != null) 78 | source.warning(msg); 79 | else 80 | throw new LexerException(msg); 81 | } 82 | 83 | private char trigraph(char raw, char repl) 84 | throws IOException, LexerException { 85 | if (trigraphs) { 86 | if (warnings) 87 | warning("trigraph ??" + raw + " converted to " + repl); 88 | return repl; 89 | } else { 90 | if (warnings) 91 | warning("trigraph ??" + raw + " ignored"); 92 | _unread(raw); 93 | _unread('?'); 94 | return '?'; 95 | } 96 | } 97 | 98 | private int _read() 99 | throws IOException, LexerException { 100 | int c = __read(); 101 | if (c == '?' && (trigraphs || warnings)) { 102 | int d = __read(); 103 | if (d == '?') { 104 | int e = __read(); 105 | switch (e) { 106 | case '(': 107 | return trigraph('(', '['); 108 | case ')': 109 | return trigraph(')', ']'); 110 | case '<': 111 | return trigraph('<', '{'); 112 | case '>': 113 | return trigraph('>', '}'); 114 | case '=': 115 | return trigraph('=', '#'); 116 | case '/': 117 | return trigraph('/', '\\'); 118 | case '\'': 119 | return trigraph('\'', '^'); 120 | case '!': 121 | return trigraph('!', '|'); 122 | case '-': 123 | return trigraph('-', '~'); 124 | } 125 | _unread(e); 126 | } 127 | _unread(d); 128 | } 129 | return c; 130 | } 131 | 132 | public int read() 133 | throws IOException, LexerException { 134 | if (flushnl) { 135 | if (newlines > 0) { 136 | newlines--; 137 | return '\n'; 138 | } 139 | flushnl = false; 140 | } 141 | 142 | for (;;) { 143 | int c = _read(); 144 | switch (c) { 145 | case '\\': 146 | int d = _read(); 147 | switch (d) { 148 | case '\n': 149 | newlines++; 150 | continue; 151 | case '\r': 152 | newlines++; 153 | int e = _read(); 154 | if (e != '\n') 155 | _unread(e); 156 | continue; 157 | default: 158 | _unread(d); 159 | return c; 160 | } 161 | case '\r': 162 | case '\n': 163 | case '\u2028': 164 | case '\u2029': 165 | case '\u000B': 166 | case '\u000C': 167 | case '\u0085': 168 | flushnl = true; 169 | return c; 170 | case -1: 171 | if (newlines > 0) { 172 | newlines--; 173 | return '\n'; 174 | } 175 | default: 176 | return c; 177 | } 178 | } 179 | } 180 | 181 | public int read(char cbuf[], int off, int len) 182 | throws IOException, LexerException { 183 | for (int i = 0; i < len; i++) { 184 | int ch = read(); 185 | if (ch == -1) 186 | return i; 187 | cbuf[off + i] = (char) ch; 188 | } 189 | return len; 190 | } 191 | 192 | @Override 193 | public void close() 194 | throws IOException { 195 | in.close(); 196 | } 197 | 198 | @Override 199 | public String toString() { 200 | return "JoinReader(nl=" + newlines + ")"; 201 | } 202 | 203 | /* 204 | public static void main(String[] args) throws IOException { 205 | FileReader f = new FileReader(new File(args[0])); 206 | BufferedReader b = new BufferedReader(f); 207 | JoinReader r = new JoinReader(b); 208 | BufferedWriter w = new BufferedWriter( 209 | new java.io.OutputStreamWriter(System.out) 210 | ); 211 | int c; 212 | while ((c = r.read()) != -1) { 213 | w.write((char)c); 214 | } 215 | w.close(); 216 | } 217 | */ 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/LexerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | /** 20 | * A preprocessor exception. 21 | * 22 | * Note to users: I don't really like the name of this class. S. 23 | */ 24 | public class LexerException extends Exception { 25 | 26 | public LexerException(String msg) { 27 | super(msg); 28 | } 29 | 30 | public LexerException(Throwable cause) { 31 | super(cause); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Macro.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | 23 | /** 24 | * A macro object. 25 | * 26 | * This encapsulates a name, an argument count, and a token stream 27 | * for replacement. The replacement token stream may contain the 28 | * extra tokens {@link Token#M_ARG} and {@link Token#M_STRING}. 29 | */ 30 | public class Macro { 31 | 32 | private Source source; 33 | private String name; 34 | /* It's an explicit decision to keep these around here. We don't 35 | * need to; the argument token type is M_ARG and the value 36 | * is the index. The strings themselves are only used in 37 | * stringification of the macro, for debugging. */ 38 | private List args; 39 | private boolean variadic; 40 | private List tokens; 41 | 42 | public Macro(Source source, String name) { 43 | this.source = source; 44 | this.name = name; 45 | this.args = null; 46 | this.variadic = false; 47 | this.tokens = new ArrayList(); 48 | } 49 | 50 | public Macro(String name) { 51 | this(null, name); 52 | } 53 | 54 | /** 55 | * Sets the Source from which this macro was parsed. 56 | */ 57 | public void setSource(Source s) { 58 | this.source = s; 59 | } 60 | 61 | /** 62 | * Returns the Source from which this macro was parsed. 63 | * 64 | * This method may return null if the macro was not parsed 65 | * from a regular file. 66 | */ 67 | public Source getSource() { 68 | return source; 69 | } 70 | 71 | /** 72 | * Returns the name of this macro. 73 | */ 74 | public String getName() { 75 | return name; 76 | } 77 | 78 | /** 79 | * Sets the arguments to this macro. 80 | */ 81 | public void setArgs(List args) { 82 | this.args = args; 83 | } 84 | 85 | /** 86 | * Returns true if this is a function-like macro. 87 | */ 88 | public boolean isFunctionLike() { 89 | return args != null; 90 | } 91 | 92 | /** 93 | * Returns the number of arguments to this macro. 94 | */ 95 | public int getArgs() { 96 | return args.size(); 97 | } 98 | 99 | /** 100 | * Sets the variadic flag on this Macro. 101 | */ 102 | public void setVariadic(boolean b) { 103 | this.variadic = b; 104 | } 105 | 106 | /** 107 | * Returns true if this is a variadic function-like macro. 108 | */ 109 | public boolean isVariadic() { 110 | return variadic; 111 | } 112 | 113 | /** 114 | * Adds a token to the expansion of this macro. 115 | */ 116 | public void addToken(Token tok) { 117 | this.tokens.add(tok); 118 | } 119 | 120 | /** 121 | * Adds a "paste" operator to the expansion of this macro. 122 | * 123 | * A paste operator causes the next token added to be pasted 124 | * to the previous token when the macro is expanded. 125 | * It is an error for a macro to end with a paste token. 126 | */ 127 | public void addPaste(Token tok) { 128 | /* 129 | * Given: tok0 ## tok1 130 | * We generate: M_PASTE, tok0, tok1 131 | * This extends as per a stack language: 132 | * tok0 ## tok1 ## tok2 -> 133 | * M_PASTE, tok0, M_PASTE, tok1, tok2 134 | */ 135 | this.tokens.add(tokens.size() - 1, tok); 136 | } 137 | 138 | /* pp */ List getTokens() { 139 | return tokens; 140 | } 141 | 142 | /* Paste tokens are inserted before the first of the two pasted 143 | * tokens, so it's a kind of bytecode notation. This method 144 | * swaps them around again. We know that there will never be two 145 | * sequential paste tokens, so a boolean is sufficient. */ 146 | public String getText() { 147 | StringBuilder buf = new StringBuilder(); 148 | boolean paste = false; 149 | for (Token tok : tokens) { 150 | if (tok.getType() == Token.M_PASTE) { 151 | assert paste == false : "Two sequential pastes."; 152 | paste = true; 153 | continue; 154 | } else { 155 | buf.append(tok.getText()); 156 | } 157 | if (paste) { 158 | buf.append(" #" + "# "); 159 | paste = false; 160 | } 161 | // buf.append(tokens.get(i)); 162 | } 163 | return buf.toString(); 164 | } 165 | 166 | @Override 167 | public String toString() { 168 | StringBuilder buf = new StringBuilder(name); 169 | if (args != null) { 170 | buf.append('('); 171 | Iterator it = args.iterator(); 172 | while (it.hasNext()) { 173 | buf.append(it.next()); 174 | if (it.hasNext()) 175 | buf.append(", "); 176 | else if (isVariadic()) 177 | buf.append("..."); 178 | } 179 | buf.append(')'); 180 | } 181 | if (!tokens.isEmpty()) { 182 | buf.append(" => ").append(getText()); 183 | } 184 | return buf.toString(); 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/MacroTokenSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.IOException; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | import javax.annotation.Nonnegative; 23 | import javax.annotation.Nonnull; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import static org.anarres.cpp.Token.*; 27 | 28 | /* This source should always be active, since we don't expand macros 29 | * in any inactive context. */ 30 | /* pp */ class MacroTokenSource extends Source { 31 | 32 | private static final Logger LOG = LoggerFactory.getLogger(MacroTokenSource.class); 33 | private final Macro macro; 34 | private final Iterator tokens; /* Pointer into the macro. */ 35 | 36 | private final List args; /* { unexpanded, expanded } */ 37 | 38 | private Iterator arg; /* "current expansion" */ 39 | 40 | /* pp */ MacroTokenSource(@Nonnull Macro m, @Nonnull List args) { 41 | this.macro = m; 42 | this.tokens = m.getTokens().iterator(); 43 | this.args = args; 44 | this.arg = null; 45 | } 46 | 47 | @Override 48 | /* pp */ boolean isExpanding(@Nonnull Macro m) { 49 | /* When we are expanding an arg, 'this' macro is not 50 | * being expanded, and thus we may re-expand it. */ 51 | if (/* XXX this.arg == null && */this.macro == m) 52 | return true; 53 | return super.isExpanding(m); 54 | } 55 | 56 | /* XXX Called from Preprocessor [ugly]. */ 57 | /* pp */ static void escape(@Nonnull StringBuilder buf, @Nonnull CharSequence cs) { 58 | if (buf == null) 59 | throw new NullPointerException("Buffer was null."); 60 | if (cs == null) 61 | throw new NullPointerException("CharSequence was null."); 62 | for (int i = 0; i < cs.length(); i++) { 63 | char c = cs.charAt(i); 64 | switch (c) { 65 | case '\\': 66 | buf.append("\\\\"); 67 | break; 68 | case '"': 69 | buf.append("\\\""); 70 | break; 71 | case '\n': 72 | buf.append("\\n"); 73 | break; 74 | case '\r': 75 | buf.append("\\r"); 76 | break; 77 | default: 78 | buf.append(c); 79 | } 80 | } 81 | } 82 | 83 | private void concat(@Nonnull StringBuilder buf, @Nonnull Argument arg) { 84 | for (Token tok : arg) { 85 | buf.append(tok.getText()); 86 | } 87 | } 88 | 89 | @Nonnull 90 | private Token stringify(@Nonnull Token pos, @Nonnull Argument arg) { 91 | StringBuilder buf = new StringBuilder(); 92 | concat(buf, arg); 93 | // System.out.println("Concat: " + arg + " -> " + buf); 94 | StringBuilder str = new StringBuilder("\""); 95 | escape(str, buf); 96 | str.append("\""); 97 | // System.out.println("Escape: " + buf + " -> " + str); 98 | return new Token(STRING, 99 | pos.getLine(), pos.getColumn(), 100 | str.toString(), buf.toString()); 101 | } 102 | 103 | /** 104 | * Returns true if the given argumentIndex is the last argument of a variadic macro. 105 | * 106 | * @param argumentIndex The index of the argument to inspect. 107 | * @return true if the given argumentIndex is the last argument of a variadic macro. 108 | */ 109 | private boolean isVariadicArgument(@Nonnegative int argumentIndex) { 110 | if (!macro.isVariadic()) 111 | return false; 112 | return argumentIndex == args.size() - 1; 113 | } 114 | 115 | /* At this point, we have consumed the first M_PASTE. 116 | * @see Macro#addPaste(Token) */ 117 | private void paste(@Nonnull Token ptok) 118 | throws IOException, 119 | LexerException { 120 | // List out = new ArrayList(); 121 | StringBuilder buf = new StringBuilder(); 122 | // Token err = null; 123 | /* We know here that arg is null or expired, 124 | * since we cannot paste an expanded arg. */ 125 | 126 | int count = 2; 127 | // While I hate auxiliary booleans, this does actually seem to be the simplest solution, 128 | // as it avoids duplicating all the logic around hasNext() in case COMMA. 129 | boolean comma = false; 130 | TOKEN: 131 | for (int i = 0; i < count; i++) { 132 | if (!tokens.hasNext()) { 133 | /* XXX This one really should throw. */ 134 | error(ptok.getLine(), ptok.getColumn(), 135 | "Paste at end of expansion"); 136 | buf.append(' ').append(ptok.getText()); 137 | break; 138 | } 139 | Token tok = tokens.next(); 140 | // System.out.println("Paste " + tok); 141 | switch (tok.getType()) { 142 | case M_PASTE: 143 | /* One extra to paste, plus one because the 144 | * paste token didn't count. */ 145 | count += 2; 146 | ptok = tok; 147 | break; 148 | case M_ARG: 149 | int idx = ((Integer) tok.getValue()).intValue(); 150 | Argument arg = args.get(idx); 151 | if (comma && isVariadicArgument(idx) && arg.isEmpty()) { 152 | // Ugly way to strip the comma. 153 | buf.setLength(buf.length() - 1); 154 | } else { 155 | concat(buf, arg); 156 | } 157 | break; 158 | /* XXX Test this. */ 159 | case CCOMMENT: 160 | case CPPCOMMENT: 161 | // TODO: In cpp, -CC keeps these comments too, 162 | // but turns all C++ comments into C comments. 163 | break; 164 | case ',': 165 | comma = true; 166 | buf.append(tok.getText()); 167 | continue TOKEN; 168 | default: 169 | buf.append(tok.getText()); 170 | break; 171 | } 172 | comma = false; 173 | } 174 | 175 | /* Push and re-lex. */ 176 | /* 177 | StringBuilder src = new StringBuilder(); 178 | escape(src, buf); 179 | StringLexerSource sl = new StringLexerSource(src.toString()); 180 | */ 181 | StringLexerSource sl = new StringLexerSource(buf.toString()); 182 | 183 | /* XXX Check that concatenation produces a valid token. */ 184 | arg = new SourceIterator(sl); 185 | } 186 | 187 | @Override 188 | public Token token() 189 | throws IOException, 190 | LexerException { 191 | for (;;) { 192 | /* Deal with lexed tokens first. */ 193 | 194 | if (arg != null) { 195 | if (arg.hasNext()) { 196 | Token tok = arg.next(); 197 | /* XXX PASTE -> INVALID. */ 198 | assert tok.getType() != M_PASTE : 199 | "Unexpected paste token"; 200 | return tok; 201 | } 202 | arg = null; 203 | } 204 | 205 | if (!tokens.hasNext()) 206 | return new Token(EOF, -1, -1, ""); /* End of macro. */ 207 | 208 | Token tok = tokens.next(); 209 | int idx; 210 | switch (tok.getType()) { 211 | case M_STRING: 212 | /* Use the nonexpanded arg. */ 213 | idx = ((Integer) tok.getValue()).intValue(); 214 | return stringify(tok, args.get(idx)); 215 | case M_ARG: 216 | /* Expand the arg. */ 217 | idx = ((Integer) tok.getValue()).intValue(); 218 | // System.out.println("Pushing arg " + args.get(idx)); 219 | arg = args.get(idx).expansion(); 220 | break; 221 | case M_PASTE: 222 | paste(tok); 223 | break; 224 | default: 225 | return tok; 226 | } 227 | } /* for */ 228 | 229 | } 230 | 231 | @Override 232 | public String toString() { 233 | StringBuilder buf = new StringBuilder(); 234 | buf.append("expansion of ").append(macro.getName()); 235 | Source parent = getParent(); 236 | if (parent != null) 237 | buf.append(" in ").append(String.valueOf(parent)); 238 | return buf.toString(); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.File; 20 | import java.io.PrintStream; 21 | import java.util.Arrays; 22 | import java.util.EnumSet; 23 | import java.util.List; 24 | import javax.annotation.Nonnull; 25 | import joptsimple.OptionParser; 26 | import joptsimple.OptionSet; 27 | import joptsimple.OptionSpec; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | /** 32 | * (Currently a simple test class). 33 | */ 34 | public class Main { 35 | 36 | private static final Logger LOG = LoggerFactory.getLogger(Main.class); 37 | 38 | @Nonnull 39 | private static CharSequence getWarnings() { 40 | StringBuilder buf = new StringBuilder(); 41 | for (Warning w : Warning.values()) { 42 | if (buf.length() > 0) 43 | buf.append(", "); 44 | String name = w.name().toLowerCase(); 45 | buf.append(name.replace('_', '-')); 46 | } 47 | return buf; 48 | } 49 | 50 | public static void main(String[] args) throws Exception { 51 | (new Main()).run(args); 52 | } 53 | 54 | public void run(String[] args) throws Exception { 55 | 56 | OptionParser parser = new OptionParser(); 57 | OptionSpec helpOption = parser.accepts("help", 58 | "Displays command-line help.") 59 | .forHelp(); 60 | OptionSpec versionOption = parser.acceptsAll(Arrays.asList("version"), 61 | "Displays the product version (" + BuildMetadata.getInstance().getVersion() + ") and exits.") 62 | .forHelp(); 63 | 64 | OptionSpec debugOption = parser.acceptsAll(Arrays.asList("debug"), 65 | "Enables debug output."); 66 | 67 | OptionSpec defineOption = parser.acceptsAll(Arrays.asList("define", "D"), 68 | "Defines the given macro.") 69 | .withRequiredArg().ofType(String.class).describedAs("name[=definition]"); 70 | OptionSpec undefineOption = parser.acceptsAll(Arrays.asList("undefine", "U"), 71 | "Undefines the given macro, previously either builtin or defined using -D.") 72 | .withRequiredArg().describedAs("name"); 73 | OptionSpec includeOption = parser.accepts("include", 74 | "Process file as if \"#" + "include \"file\"\" appeared as the first line of the primary source file.") 75 | .withRequiredArg().ofType(File.class).describedAs("file"); 76 | OptionSpec incdirOption = parser.acceptsAll(Arrays.asList("incdir", "I"), 77 | "Adds the directory dir to the list of directories to be searched for header files.") 78 | .withRequiredArg().ofType(File.class).describedAs("dir"); 79 | OptionSpec iquoteOption = parser.acceptsAll(Arrays.asList("iquote"), 80 | "Adds the directory dir to the list of directories to be searched for header files included using \"\".") 81 | .withRequiredArg().ofType(File.class).describedAs("dir"); 82 | OptionSpec warningOption = parser.acceptsAll(Arrays.asList("warning", "W"), 83 | "Enables the named warning class (" + getWarnings() + ").") 84 | .withRequiredArg().ofType(String.class).describedAs("warning"); 85 | OptionSpec noWarningOption = parser.acceptsAll(Arrays.asList("no-warnings", "w"), 86 | "Disables ALL warnings."); 87 | OptionSpec inputsOption = parser.nonOptions() 88 | .ofType(File.class).describedAs("Files to process."); 89 | 90 | OptionSet options = parser.parse(args); 91 | 92 | if (options.has(helpOption)) { 93 | parser.printHelpOn(System.out); 94 | return; 95 | } 96 | 97 | if (options.has(versionOption)) { 98 | version(System.out); 99 | return; 100 | } 101 | 102 | Preprocessor pp = new Preprocessor(); 103 | pp.addFeature(Feature.DIGRAPHS); 104 | pp.addFeature(Feature.TRIGRAPHS); 105 | pp.addFeature(Feature.LINEMARKERS); 106 | pp.addWarning(Warning.IMPORT); 107 | pp.setListener(new DefaultPreprocessorListener()); 108 | pp.addMacro("__JCPP__"); 109 | pp.getSystemIncludePath().add("/usr/local/include"); 110 | pp.getSystemIncludePath().add("/usr/include"); 111 | pp.getFrameworksPath().add("/System/Library/Frameworks"); 112 | pp.getFrameworksPath().add("/Library/Frameworks"); 113 | pp.getFrameworksPath().add("/Local/Library/Frameworks"); 114 | 115 | if (options.has(debugOption)) 116 | pp.addFeature(Feature.DEBUG); 117 | 118 | if (options.has(noWarningOption)) 119 | pp.getWarnings().clear(); 120 | 121 | for (String warning : options.valuesOf(warningOption)) { 122 | warning = warning.toUpperCase(); 123 | warning = warning.replace('-', '_'); 124 | if (warning.equals("ALL")) 125 | pp.addWarnings(EnumSet.allOf(Warning.class)); 126 | else 127 | pp.addWarning(Enum.valueOf(Warning.class, warning)); 128 | } 129 | 130 | for (String arg : options.valuesOf(defineOption)) { 131 | int idx = arg.indexOf('='); 132 | if (idx == -1) 133 | pp.addMacro(arg); 134 | else 135 | pp.addMacro(arg.substring(0, idx), arg.substring(idx + 1)); 136 | } 137 | for (String arg : options.valuesOf(undefineOption)) { 138 | pp.getMacros().remove(arg); 139 | } 140 | 141 | for (File dir : options.valuesOf(incdirOption)) 142 | pp.getSystemIncludePath().add(dir.getAbsolutePath()); 143 | for (File dir : options.valuesOf(iquoteOption)) 144 | pp.getQuoteIncludePath().add(dir.getAbsolutePath()); 145 | for (File file : options.valuesOf(includeOption)) 146 | // Comply exactly with spec. 147 | pp.addInput(new StringLexerSource("#" + "include \"" + file + "\"\n")); 148 | 149 | List inputs = options.valuesOf(inputsOption); 150 | if (inputs.isEmpty()) { 151 | pp.addInput(new InputLexerSource(System.in)); 152 | } else { 153 | for (File input : inputs) 154 | pp.addInput(new FileLexerSource(input)); 155 | } 156 | 157 | if (pp.getFeature(Feature.DEBUG)) { 158 | LOG.info("#" + "include \"...\" search starts here:"); 159 | for (String dir : pp.getQuoteIncludePath()) 160 | LOG.info(" " + dir); 161 | LOG.info("#" + "include <...> search starts here:"); 162 | for (String dir : pp.getSystemIncludePath()) 163 | LOG.info(" " + dir); 164 | LOG.info("End of search list."); 165 | } 166 | 167 | try { 168 | for (;;) { 169 | Token tok = pp.token(); 170 | if (tok == null) 171 | break; 172 | if (tok.getType() == Token.EOF) 173 | break; 174 | System.out.print(tok.getText()); 175 | } 176 | } catch (Exception e) { 177 | StringBuilder buf = new StringBuilder("Preprocessor failed:\n"); 178 | Source s = pp.getSource(); 179 | while (s != null) { 180 | buf.append(" -> ").append(s).append("\n"); 181 | s = s.getParent(); 182 | } 183 | LOG.error(buf.toString(), e); 184 | } 185 | 186 | } 187 | 188 | private static void version(@Nonnull PrintStream out) { 189 | BuildMetadata metadata = BuildMetadata.getInstance(); 190 | out.println("Anarres Java C Preprocessor version " + metadata.getVersion() + " change-id " + metadata.getChangeId()); 191 | out.println("Copyright (C) 2007-2015 Shevek (http://www.anarres.org/)."); 192 | out.println("This is free software; see the source for copying conditions. There is NO"); 193 | out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/NumericValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.math.BigDecimal; 20 | import java.math.BigInteger; 21 | import javax.annotation.CheckForNull; 22 | import javax.annotation.CheckForSigned; 23 | import javax.annotation.Nonnegative; 24 | import javax.annotation.Nonnull; 25 | 26 | public class NumericValue extends Number { 27 | 28 | public static final int F_UNSIGNED = 1; 29 | public static final int F_INT = 2; 30 | public static final int F_LONG = 4; 31 | public static final int F_LONGLONG = 8; 32 | public static final int F_FLOAT = 16; 33 | public static final int F_DOUBLE = 32; 34 | 35 | public static final int FF_SIZE = F_INT | F_LONG | F_LONGLONG | F_FLOAT | F_DOUBLE; 36 | 37 | private final int base; 38 | private final String integer; 39 | private String fraction; 40 | private int expbase = 0; 41 | private String exponent; 42 | private int flags; 43 | 44 | public NumericValue(@Nonnegative int base, @Nonnull String integer) { 45 | this.base = base; 46 | this.integer = integer; 47 | } 48 | 49 | @Nonnegative 50 | public int getBase() { 51 | return base; 52 | } 53 | 54 | @Nonnull 55 | public String getIntegerPart() { 56 | return integer; 57 | } 58 | 59 | @CheckForNull 60 | public String getFractionalPart() { 61 | return fraction; 62 | } 63 | 64 | /* pp */ void setFractionalPart(@Nonnull String fraction) { 65 | this.fraction = fraction; 66 | } 67 | 68 | @CheckForSigned 69 | public int getExponentBase() { 70 | return expbase; 71 | } 72 | 73 | @CheckForNull 74 | public String getExponent() { 75 | return exponent; 76 | } 77 | 78 | /* pp */ void setExponent(@Nonnegative int expbase, @Nonnull String exponent) { 79 | this.expbase = expbase; 80 | this.exponent = exponent; 81 | } 82 | 83 | public int getFlags() { 84 | return flags; 85 | } 86 | 87 | /* pp */ void setFlags(int flags) { 88 | this.flags = flags; 89 | } 90 | 91 | /** 92 | * So, it turns out that parsing arbitrary bases into arbitrary 93 | * precision numbers is nontrivial, and this routine gets it wrong 94 | * in many important cases. 95 | */ 96 | @Nonnull 97 | public BigDecimal toBigDecimal() { 98 | int scale = 0; 99 | String text = getIntegerPart(); 100 | String t_fraction = getFractionalPart(); 101 | if (t_fraction != null) { 102 | text += getFractionalPart(); 103 | // XXX Wrong for anything but base 10. 104 | scale += t_fraction.length(); 105 | } 106 | String t_exponent = getExponent(); 107 | if (t_exponent != null) 108 | scale -= Integer.parseInt(t_exponent); 109 | BigInteger unscaled = new BigInteger(text, getBase()); 110 | return new BigDecimal(unscaled, scale); 111 | } 112 | 113 | // We could construct a heuristic for when an 'int' is large enough. 114 | // private static final int S_MAXLEN_LONG = String.valueOf(Long.MAX_VALUE).length(); 115 | // private static final int S_MAXLEN_INT = String.valueOf(Integer.MAX_VALUE).length(); 116 | 117 | @Nonnull 118 | public Number toJavaLangNumber() { 119 | int flags = getFlags(); 120 | if ((flags & F_DOUBLE) != 0) 121 | return doubleValue(); 122 | else if ((flags & F_FLOAT) != 0) 123 | return floatValue(); 124 | else if ((flags & (F_LONG | F_LONGLONG)) != 0) 125 | return longValue(); 126 | else if ((flags & F_INT) != 0) 127 | return intValue(); 128 | else if (getFractionalPart() != null) 129 | return doubleValue(); // .1 is a double in Java. 130 | else if (getExponent() != null) 131 | return doubleValue(); 132 | else { 133 | // This is an attempt to avoid overflowing on over-long integers. 134 | // However, now we just overflow on over-long longs. 135 | // We should really use BigInteger. 136 | long value = longValue(); 137 | if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) 138 | return (int) value; 139 | return value; 140 | } 141 | } 142 | 143 | private int exponentValue() { 144 | return Integer.parseInt(exponent, 10); 145 | } 146 | 147 | @Override 148 | public int intValue() { 149 | // String.isEmpty() is since 1.6 150 | int v = integer.length() == 0 ? 0 : Integer.parseInt(integer, base); 151 | if (expbase == 2) 152 | v = v << exponentValue(); 153 | else if (expbase != 0) 154 | v = (int) (v * Math.pow(expbase, exponentValue())); 155 | return v; 156 | } 157 | 158 | @Override 159 | public long longValue() { 160 | // String.isEmpty() is since 1.6 161 | long v = integer.length() == 0 ? 0 : Long.parseLong(integer, base); 162 | if (expbase == 2) 163 | v = v << exponentValue(); 164 | else if (expbase != 0) 165 | v = (long) (v * Math.pow(expbase, exponentValue())); 166 | return v; 167 | } 168 | 169 | @Override 170 | public float floatValue() { 171 | if (getBase() != 10) 172 | return longValue(); 173 | return Float.parseFloat(toString()); 174 | } 175 | 176 | @Override 177 | public double doubleValue() { 178 | if (getBase() != 10) 179 | return longValue(); 180 | return Double.parseDouble(toString()); 181 | } 182 | 183 | private boolean appendFlags(StringBuilder buf, String suffix, int flag) { 184 | if ((getFlags() & flag) != flag) 185 | return false; 186 | buf.append(suffix); 187 | return true; 188 | } 189 | 190 | @Override 191 | public String toString() { 192 | StringBuilder buf = new StringBuilder(); 193 | switch (base) { 194 | case 8: 195 | buf.append('0'); 196 | break; 197 | case 10: 198 | break; 199 | case 16: 200 | buf.append("0x"); 201 | break; 202 | case 2: 203 | buf.append('b'); 204 | break; 205 | default: 206 | buf.append("[base-").append(base).append("]"); 207 | break; 208 | } 209 | buf.append(getIntegerPart()); 210 | if (getFractionalPart() != null) 211 | buf.append('.').append(getFractionalPart()); 212 | if (getExponent() != null) { 213 | buf.append(base > 10 ? 'p' : 'e'); 214 | buf.append(getExponent()); 215 | } 216 | /* 217 | if (appendFlags(buf, "ui", F_UNSIGNED | F_INT)); 218 | else if (appendFlags(buf, "ul", F_UNSIGNED | F_LONG)); 219 | else if (appendFlags(buf, "ull", F_UNSIGNED | F_LONGLONG)); 220 | else if (appendFlags(buf, "i", F_INT)); 221 | else if (appendFlags(buf, "l", F_LONG)); 222 | else if (appendFlags(buf, "ll", F_LONGLONG)); 223 | else if (appendFlags(buf, "f", F_FLOAT)); 224 | else if (appendFlags(buf, "d", F_DOUBLE)); 225 | */ 226 | return buf.toString(); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/PreprocessorCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.anarres.cpp; 7 | 8 | import javax.annotation.CheckForNull; 9 | import javax.annotation.Nonnull; 10 | 11 | /** 12 | * 13 | * @author shevek 14 | */ 15 | public enum PreprocessorCommand { 16 | 17 | PP_DEFINE("define"), 18 | PP_ELIF("elif"), 19 | PP_ELSE("else"), 20 | PP_ENDIF("endif"), 21 | PP_ERROR("error"), 22 | PP_IF("if"), 23 | PP_IFDEF("ifdef"), 24 | PP_IFNDEF("ifndef"), 25 | PP_INCLUDE("include"), 26 | PP_LINE("line"), 27 | PP_PRAGMA("pragma"), 28 | PP_UNDEF("undef"), 29 | PP_WARNING("warning"), 30 | PP_INCLUDE_NEXT("include_next"), 31 | PP_IMPORT("import"); 32 | private final String text; 33 | /* pp */ PreprocessorCommand(String text) { 34 | this.text = text; 35 | } 36 | 37 | @CheckForNull 38 | public static PreprocessorCommand forText(@Nonnull String text) { 39 | for (PreprocessorCommand ppcmd : PreprocessorCommand.values()) 40 | if (ppcmd.text.equals(text)) 41 | return ppcmd; 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/PreprocessorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import javax.annotation.Nonnull; 20 | 21 | /** 22 | * A handler for preprocessor events, primarily errors and warnings. 23 | * 24 | * If no PreprocessorListener is installed in a Preprocessor, all 25 | * error and warning events will throw an exception. Installing a 26 | * listener allows more intelligent handling of these events. 27 | */ 28 | public interface PreprocessorListener { 29 | 30 | /** 31 | * Handles a warning. 32 | * 33 | * The behaviour of this method is defined by the 34 | * implementation. It may simply record the error message, or 35 | * it may throw an exception. 36 | */ 37 | public void handleWarning(@Nonnull Source source, int line, int column, 38 | @Nonnull String msg) 39 | throws LexerException; 40 | 41 | /** 42 | * Handles an error. 43 | * 44 | * The behaviour of this method is defined by the 45 | * implementation. It may simply record the error message, or 46 | * it may throw an exception. 47 | */ 48 | public void handleError(@Nonnull Source source, int line, int column, 49 | @Nonnull String msg) 50 | throws LexerException; 51 | 52 | public enum SourceChangeEvent { 53 | 54 | SUSPEND, PUSH, POP, RESUME; 55 | } 56 | 57 | public void handleSourceChange(@Nonnull Source source, @Nonnull SourceChangeEvent event); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/ResourceFileSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.anarres.cpp; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.nio.charset.Charset; 11 | import javax.annotation.Nonnull; 12 | 13 | /** 14 | * 15 | * @author shevek 16 | */ 17 | public class ResourceFileSystem implements VirtualFileSystem { 18 | 19 | private final ClassLoader loader; 20 | private final Charset charset; 21 | 22 | public ResourceFileSystem(@Nonnull ClassLoader loader, @Nonnull Charset charset) { 23 | this.loader = loader; 24 | this.charset = charset; 25 | } 26 | 27 | @Override 28 | public VirtualFile getFile(String path) { 29 | return new ResourceFile(loader, path); 30 | } 31 | 32 | @Override 33 | public VirtualFile getFile(String dir, String name) { 34 | return getFile(dir + "/" + name); 35 | } 36 | 37 | private class ResourceFile implements VirtualFile { 38 | 39 | private final ClassLoader loader; 40 | private final String path; 41 | 42 | public ResourceFile(ClassLoader loader, String path) { 43 | this.loader = loader; 44 | this.path = path; 45 | } 46 | 47 | @Override 48 | public boolean isFile() { 49 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 50 | } 51 | 52 | @Override 53 | public String getPath() { 54 | return path; 55 | } 56 | 57 | @Override 58 | public String getName() { 59 | return path.substring(path.lastIndexOf('/') + 1); 60 | } 61 | 62 | @Override 63 | public ResourceFile getParentFile() { 64 | int idx = path.lastIndexOf('/'); 65 | if (idx < 1) 66 | return null; 67 | return new ResourceFile(loader, path.substring(0, idx)); 68 | } 69 | 70 | @Override 71 | public ResourceFile getChildFile(String name) { 72 | return new ResourceFile(loader, path + "/" + name); 73 | } 74 | 75 | @Override 76 | public Source getSource() throws IOException { 77 | InputStream stream = loader.getResourceAsStream(path); 78 | return new InputLexerSource(stream, charset); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Source.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.Closeable; 20 | import java.io.IOException; 21 | import java.util.Iterator; 22 | import javax.annotation.CheckForNull; 23 | import javax.annotation.Nonnegative; 24 | import javax.annotation.Nonnull; 25 | import static org.anarres.cpp.Token.CCOMMENT; 26 | import static org.anarres.cpp.Token.CPPCOMMENT; 27 | import static org.anarres.cpp.Token.EOF; 28 | import static org.anarres.cpp.Token.NL; 29 | import static org.anarres.cpp.Token.WHITESPACE; 30 | 31 | /** 32 | * An input to the Preprocessor. 33 | * 34 | * Inputs may come from Files, Strings or other sources. The 35 | * preprocessor maintains a stack of Sources. Operations such as 36 | * file inclusion or token pasting will push a new source onto 37 | * the Preprocessor stack. Sources pop from the stack when they 38 | * are exhausted; this may be transparent or explicit. 39 | * 40 | * BUG: Error messages are not handled properly. 41 | */ 42 | public abstract class Source implements Iterable, Closeable { 43 | 44 | private Source parent; 45 | private boolean autopop; 46 | private PreprocessorListener listener; 47 | private boolean active; 48 | private boolean werror; 49 | 50 | /* LineNumberReader */ 51 | 52 | /* 53 | // We can't do this, since we would lose the LexerException 54 | private class Itr implements Iterator { 55 | private Token next = null; 56 | private void advance() { 57 | try { 58 | if (next != null) 59 | next = token(); 60 | } 61 | catch (IOException e) { 62 | throw new UnsupportedOperationException( 63 | "Failed to advance token iterator: " + 64 | e.getMessage() 65 | ); 66 | } 67 | } 68 | public boolean hasNext() { 69 | return next.getType() != EOF; 70 | } 71 | public Token next() { 72 | advance(); 73 | Token t = next; 74 | next = null; 75 | return t; 76 | } 77 | public void remove() { 78 | throw new UnsupportedOperationException( 79 | "Cannot remove tokens from a Source." 80 | ); 81 | } 82 | } 83 | */ 84 | public Source() { 85 | this.parent = null; 86 | this.autopop = false; 87 | this.listener = null; 88 | this.active = true; 89 | this.werror = false; 90 | } 91 | 92 | /** 93 | * Sets the parent source of this source. 94 | * 95 | * Sources form a singly linked list. 96 | */ 97 | /* pp */ void setParent(Source parent, boolean autopop) { 98 | this.parent = parent; 99 | this.autopop = autopop; 100 | } 101 | 102 | /** 103 | * Returns the parent source of this source. 104 | * 105 | * Sources form a singly linked list. 106 | */ 107 | /* pp */ final Source getParent() { 108 | return parent; 109 | } 110 | 111 | 112 | // @OverrideMustInvoke 113 | /* pp */ void init(Preprocessor pp) { 114 | setListener(pp.getListener()); 115 | this.werror = pp.getWarnings().contains(Warning.ERROR); 116 | } 117 | 118 | /** 119 | * Sets the listener for this Source. 120 | * 121 | * Normally this is set by the Preprocessor when a Source is 122 | * used, but if you are using a Source as a standalone object, 123 | * you may wish to call this. 124 | */ 125 | public void setListener(PreprocessorListener pl) { 126 | this.listener = pl; 127 | } 128 | 129 | /** 130 | * Returns the File currently being lexed. 131 | * 132 | * If this Source is not a {@link FileLexerSource}, then 133 | * it will ask the parent Source, and so forth recursively. 134 | * If no Source on the stack is a FileLexerSource, returns null. 135 | */ 136 | @CheckForNull 137 | public String getPath() { 138 | Source parent = getParent(); 139 | if (parent != null) 140 | return parent.getPath(); 141 | return null; 142 | } 143 | 144 | /** 145 | * Returns the human-readable name of the current Source. 146 | */ 147 | @CheckForNull 148 | public String getName() { 149 | Source parent = getParent(); 150 | if (parent != null) 151 | return parent.getName(); 152 | return null; 153 | } 154 | 155 | /** 156 | * Returns the current line number within this Source. 157 | */ 158 | @Nonnegative 159 | public int getLine() { 160 | Source parent = getParent(); 161 | if (parent == null) 162 | return 0; 163 | return parent.getLine(); 164 | } 165 | 166 | /** 167 | * Returns the current column number within this Source. 168 | */ 169 | public int getColumn() { 170 | Source parent = getParent(); 171 | if (parent == null) 172 | return 0; 173 | return parent.getColumn(); 174 | } 175 | 176 | /** 177 | * Returns true if this Source is expanding the given macro. 178 | * 179 | * This is used to prevent macro recursion. 180 | */ 181 | /* pp */ boolean isExpanding(@Nonnull Macro m) { 182 | Source parent = getParent(); 183 | if (parent != null) 184 | return parent.isExpanding(m); 185 | return false; 186 | } 187 | 188 | /** 189 | * Returns true if this Source should be transparently popped 190 | * from the input stack. 191 | * 192 | * Examples of such sources are macro expansions. 193 | */ 194 | /* pp */ boolean isAutopop() { 195 | return autopop; 196 | } 197 | 198 | /** 199 | * Returns true if this source has line numbers. 200 | */ 201 | /* pp */ boolean isNumbered() { 202 | return false; 203 | } 204 | 205 | /* This is an incredibly lazy way of disabling warnings when 206 | * the source is not active. */ 207 | /* pp */ void setActive(boolean b) { 208 | this.active = b; 209 | } 210 | 211 | /* pp */ boolean isActive() { 212 | return active; 213 | } 214 | 215 | /** 216 | * Returns the next Token parsed from this input stream. 217 | * 218 | * @see Token 219 | */ 220 | @Nonnull 221 | public abstract Token token() 222 | throws IOException, 223 | LexerException; 224 | 225 | /** 226 | * Returns a token iterator for this Source. 227 | */ 228 | @Override 229 | public Iterator iterator() { 230 | return new SourceIterator(this); 231 | } 232 | 233 | /** 234 | * Skips tokens until the end of line. 235 | * 236 | * @param white true if only whitespace is permitted on the 237 | * remainder of the line. 238 | * @return the NL token. 239 | */ 240 | @Nonnull 241 | public Token skipline(boolean white) 242 | throws IOException, 243 | LexerException { 244 | for (;;) { 245 | Token tok = token(); 246 | switch (tok.getType()) { 247 | case EOF: 248 | /* There ought to be a newline before EOF. 249 | * At least, in any skipline context. */ 250 | /* XXX Are we sure about this? */ 251 | warning(tok.getLine(), tok.getColumn(), 252 | "No newline before end of file"); 253 | return new Token(NL, 254 | tok.getLine(), tok.getColumn(), 255 | "\n"); 256 | // return tok; 257 | case NL: 258 | /* This may contain one or more newlines. */ 259 | return tok; 260 | case CCOMMENT: 261 | case CPPCOMMENT: 262 | case WHITESPACE: 263 | break; 264 | default: 265 | /* XXX Check white, if required. */ 266 | if (white) 267 | warning(tok.getLine(), tok.getColumn(), 268 | "Unexpected nonwhite token"); 269 | break; 270 | } 271 | } 272 | } 273 | 274 | protected void error(int line, int column, String msg) 275 | throws LexerException { 276 | if (listener != null) 277 | listener.handleError(this, line, column, msg); 278 | else 279 | throw new LexerException("Error at " + line + ":" + column + ": " + msg); 280 | } 281 | 282 | protected void warning(int line, int column, String msg) 283 | throws LexerException { 284 | if (werror) 285 | error(line, column, msg); 286 | else if (listener != null) 287 | listener.handleWarning(this, line, column, msg); 288 | else 289 | throw new LexerException("Warning at " + line + ":" + column + ": " + msg); 290 | } 291 | 292 | public void close() 293 | throws IOException { 294 | } 295 | 296 | } 297 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/SourceIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.IOException; 20 | import java.util.Iterator; 21 | import java.util.NoSuchElementException; 22 | import javax.annotation.Nonnull; 23 | import static org.anarres.cpp.Token.EOF; 24 | 25 | /** 26 | * An Iterator for {@link Source Sources}, 27 | * returning {@link Token Tokens}. 28 | */ 29 | public class SourceIterator implements Iterator { 30 | 31 | private final Source source; 32 | private Token tok; 33 | 34 | public SourceIterator(@Nonnull Source s) { 35 | this.source = s; 36 | this.tok = null; 37 | } 38 | 39 | /** 40 | * Rethrows IOException inside IllegalStateException. 41 | */ 42 | private void advance() { 43 | try { 44 | if (tok == null) 45 | tok = source.token(); 46 | } catch (LexerException e) { 47 | throw new IllegalStateException(e); 48 | } catch (IOException e) { 49 | throw new IllegalStateException(e); 50 | } 51 | } 52 | 53 | /** 54 | * Returns true if the enclosed Source has more tokens. 55 | * 56 | * The EOF token is never returned by the iterator. 57 | * @throws IllegalStateException if the Source 58 | * throws a LexerException or IOException 59 | */ 60 | @Override 61 | public boolean hasNext() { 62 | advance(); 63 | return tok.getType() != EOF; 64 | } 65 | 66 | /** 67 | * Returns the next token from the enclosed Source. 68 | * 69 | * The EOF token is never returned by the iterator. 70 | * @throws IllegalStateException if the Source 71 | * throws a LexerException or IOException 72 | */ 73 | @Override 74 | public Token next() { 75 | if (!hasNext()) 76 | throw new NoSuchElementException(); 77 | Token t = this.tok; 78 | this.tok = null; 79 | return t; 80 | } 81 | 82 | /** 83 | * Not supported. 84 | * 85 | * @throws UnsupportedOperationException unconditionally. 86 | */ 87 | @Override 88 | public void remove() { 89 | throw new UnsupportedOperationException(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/State.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | /* pp */ class State { 20 | 21 | boolean parent; 22 | boolean active; 23 | boolean sawElse; 24 | 25 | /* pp */ State() { 26 | this.parent = true; 27 | this.active = true; 28 | this.sawElse = false; 29 | } 30 | 31 | /* pp */ State(State parent) { 32 | this.parent = parent.isParentActive() && parent.isActive(); 33 | this.active = true; 34 | this.sawElse = false; 35 | } 36 | 37 | /* Required for #elif */ 38 | /* pp */ void setParentActive(boolean b) { 39 | this.parent = b; 40 | } 41 | 42 | /* pp */ boolean isParentActive() { 43 | return parent; 44 | } 45 | 46 | /* pp */ void setActive(boolean b) { 47 | this.active = b; 48 | } 49 | 50 | /* pp */ boolean isActive() { 51 | return active; 52 | } 53 | 54 | /* pp */ void setSawElse() { 55 | sawElse = true; 56 | } 57 | 58 | /* pp */ boolean sawElse() { 59 | return sawElse; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "parent=" + parent 65 | + ", active=" + active 66 | + ", sawelse=" + sawElse; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/StringLexerSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.StringReader; 20 | 21 | /** 22 | * A Source for lexing a String. 23 | * 24 | * This class is used by token pasting, but can be used by user 25 | * code. 26 | */ 27 | public class StringLexerSource extends LexerSource { 28 | 29 | /** 30 | * Creates a new Source for lexing the given String. 31 | * 32 | * @param string The input string to lex. 33 | * @param ppvalid true if preprocessor directives are to be 34 | * honoured within the string. 35 | */ 36 | public StringLexerSource(String string, boolean ppvalid) { 37 | super(new StringReader(string), ppvalid); 38 | } 39 | 40 | /** 41 | * Creates a new Source for lexing the given String. 42 | * 43 | * Equivalent to calling new StringLexerSource(string, false). 44 | * 45 | * By default, preprocessor directives are not honoured within 46 | * the string. 47 | * 48 | * @param string The input string to lex. 49 | */ 50 | public StringLexerSource(String string) { 51 | this(string, false); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "string literal"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Token.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import javax.annotation.CheckForNull; 20 | import javax.annotation.Nonnull; 21 | 22 | /** 23 | * A Preprocessor token. 24 | * 25 | * @see Preprocessor 26 | */ 27 | public final class Token { 28 | 29 | // public static final int EOF = -1; 30 | private final int type; 31 | private int line; 32 | private int column; 33 | private final Object value; 34 | private final String text; 35 | 36 | public Token(int type, int line, int column, 37 | String text, Object value) { 38 | this.type = type; 39 | this.line = line; 40 | this.column = column; 41 | this.text = text; 42 | this.value = value; 43 | } 44 | 45 | public Token(int type, int line, int column, String text) { 46 | this(type, line, column, text, null); 47 | } 48 | 49 | /* pp */ Token(int type, String text, Object value) { 50 | this(type, -1, -1, text, value); 51 | } 52 | 53 | /* pp */ Token(int type, String text) { 54 | this(type, text, null); 55 | } 56 | 57 | /* pp */ Token(int type) { 58 | this(type, TokenType.getTokenText(type)); 59 | } 60 | 61 | /** 62 | * Returns the semantic type of this token. 63 | * 64 | * @return the semantic type of this token. 65 | * @see #getTokenName(int) 66 | */ 67 | public int getType() { 68 | return type; 69 | } 70 | 71 | /* pp */ void setLocation(int line, int column) { 72 | this.line = line; 73 | this.column = column; 74 | } 75 | 76 | /** 77 | * Returns the line at which this token started. 78 | * 79 | * Lines are numbered from 1. 80 | * 81 | * @return the line at which this token started. 82 | * @see LexerSource#getLine() 83 | */ 84 | // Not @Nonnegative - might not have been assigned? 85 | public int getLine() { 86 | return line; 87 | } 88 | 89 | /** 90 | * Returns the column at which this token started. 91 | * 92 | * Columns are numbered from 0. 93 | * 94 | * @return the column at which this token started. 95 | * @see LexerSource#getColumn() 96 | */ 97 | // Not @Nonnegative - might not have been assigned? 98 | public int getColumn() { 99 | return column; 100 | } 101 | 102 | /** 103 | * Returns the original or generated text of this token. 104 | * 105 | * This is distinct from the semantic value of the token. 106 | * 107 | * @return the original or generated text of this token. 108 | * @see #getValue() 109 | */ 110 | // Not @Nonnull - might not have been assigned? 111 | public String getText() { 112 | return text; 113 | } 114 | 115 | /** 116 | * Returns the semantic value of this token. 117 | * 118 | * For strings, this is the parsed String. 119 | * For integers, this is an Integer object. 120 | * For other token types, as appropriate. 121 | * 122 | * @return the semantic value of this token, or null. 123 | * @see #getText() 124 | */ 125 | // @CheckForNull // Not useful to annotate, as we have usually checked the type before calling this. 126 | public Object getValue() { 127 | return value; 128 | } 129 | 130 | /** 131 | * Returns a description of this token, for debugging purposes. 132 | */ 133 | @Override 134 | public String toString() { 135 | StringBuilder buf = new StringBuilder(); 136 | 137 | buf.append('[').append(getTokenName(type)); 138 | if (line != -1) { 139 | buf.append('@').append(line); 140 | if (column != -1) 141 | buf.append(',').append(column); 142 | } 143 | buf.append("]:"); 144 | if (text != null) 145 | buf.append('"').append(text).append('"'); 146 | else if (type > 3 && type < 256) 147 | buf.append((char) type); 148 | else 149 | buf.append('<').append(type).append('>'); 150 | if (value != null) 151 | buf.append('=').append(value); 152 | return buf.toString(); 153 | } 154 | 155 | /** 156 | * Returns the descriptive name of the given token type. 157 | * 158 | * This is mostly used for stringification and debugging. 159 | * 160 | * @param type The type constant from this class to name. 161 | * @return the descriptive name of the given token type. 162 | * @see Token#getType() 163 | */ 164 | @Nonnull 165 | public static String getTokenName(int type) { 166 | return TokenType.getTokenName(type); 167 | } 168 | 169 | public static final int AND_EQ = 257; 170 | public static final int ARROW = 258; 171 | public static final int CHARACTER = 259; 172 | public static final int CCOMMENT = 260; 173 | public static final int CPPCOMMENT = 261; 174 | public static final int DEC = 262; 175 | public static final int DIV_EQ = 263; 176 | public static final int ELLIPSIS = 264; 177 | public static final int EOF = 265; 178 | public static final int EQ = 266; 179 | public static final int GE = 267; 180 | public static final int HASH = 268; 181 | public static final int HEADER = 269; 182 | public static final int IDENTIFIER = 270; 183 | public static final int INC = 271; 184 | public static final int NUMBER = 272; 185 | public static final int LAND = 273; 186 | public static final int LAND_EQ = 274; 187 | public static final int LE = 275; 188 | public static final int LITERAL = 276; 189 | public static final int LOR = 277; 190 | public static final int LOR_EQ = 278; 191 | public static final int LSH = 279; 192 | public static final int LSH_EQ = 280; 193 | public static final int MOD_EQ = 281; 194 | public static final int MULT_EQ = 282; 195 | public static final int NE = 283; 196 | public static final int NL = 284; 197 | public static final int OR_EQ = 285; 198 | public static final int PASTE = 286; 199 | public static final int PLUS_EQ = 287; 200 | public static final int RANGE = 288; 201 | public static final int RSH = 289; 202 | public static final int RSH_EQ = 290; 203 | public static final int SQSTRING = 291; 204 | public static final int STRING = 292; 205 | public static final int SUB_EQ = 293; 206 | public static final int WHITESPACE = 294; 207 | public static final int XOR_EQ = 295; 208 | public static final int M_ARG = 296; 209 | public static final int M_PASTE = 297; 210 | public static final int M_STRING = 298; 211 | public static final int P_LINE = 299; 212 | public static final int INVALID = 300; 213 | 214 | /** The position-less space token. */ 215 | /* pp */ static final Token space = new Token(WHITESPACE, -1, -1, " "); 216 | } 217 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/TokenSnifferSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | import static org.anarres.cpp.Token.EOF; 22 | 23 | @Deprecated 24 | /* pp */ class TokenSnifferSource extends Source { 25 | 26 | private final List target; 27 | 28 | /* pp */ TokenSnifferSource(List target) { 29 | this.target = target; 30 | } 31 | 32 | public Token token() 33 | throws IOException, 34 | LexerException { 35 | Token tok = getParent().token(); 36 | if (tok.getType() != EOF) 37 | target.add(tok); 38 | return tok; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return getParent().toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/TokenType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.anarres.cpp; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import javax.annotation.CheckForNull; 11 | import javax.annotation.Nonnegative; 12 | import javax.annotation.Nonnull; 13 | import static org.anarres.cpp.Token.*; 14 | 15 | /** 16 | * 17 | * @author shevek 18 | */ 19 | /* pp */ class TokenType { 20 | 21 | private static final List TYPES = new ArrayList(); 22 | 23 | private static void addTokenType(@Nonnegative int type, @Nonnull String name, @CheckForNull String text) { 24 | while (TYPES.size() <= type) 25 | TYPES.add(null); 26 | TYPES.set(type, new TokenType(name, text)); 27 | } 28 | 29 | private static void addTokenType(@Nonnegative int type, @Nonnull String name) { 30 | addTokenType(type, name, null); 31 | } 32 | 33 | @CheckForNull 34 | public static TokenType getTokenType(@Nonnegative int type) { 35 | try { 36 | return TYPES.get(type); 37 | } catch (IndexOutOfBoundsException e) { 38 | return null; 39 | } 40 | } 41 | 42 | @Nonnull 43 | public static String getTokenName(@Nonnegative int type) { 44 | if (type < 0) 45 | return "Invalid" + type; 46 | TokenType tokenType = getTokenType(type); 47 | if (tokenType == null) 48 | return "Unknown" + type; 49 | return tokenType.getName(); 50 | } 51 | 52 | @CheckForNull 53 | public static String getTokenText(@Nonnegative int type) { 54 | TokenType tokenType = getTokenType(type); 55 | if (tokenType == null) 56 | return null; 57 | return tokenType.getText(); 58 | } 59 | 60 | static { 61 | for (int i = 0; i < 255; i++) { 62 | String text = String.valueOf((char) i); 63 | addTokenType(i, text, text); 64 | } 65 | addTokenType(AND_EQ, "AND_EQ", "&="); 66 | addTokenType(ARROW, "ARROW", "->"); 67 | addTokenType(CHARACTER, "CHARACTER"); 68 | addTokenType(CCOMMENT, "CCOMMENT"); 69 | addTokenType(CPPCOMMENT, "CPPCOMMENT"); 70 | addTokenType(DEC, "DEC", "--"); 71 | addTokenType(DIV_EQ, "DIV_EQ", "/="); 72 | addTokenType(ELLIPSIS, "ELLIPSIS", "..."); 73 | addTokenType(EOF, "EOF"); 74 | addTokenType(EQ, "EQ", "=="); 75 | addTokenType(GE, "GE", ">="); 76 | addTokenType(HASH, "HASH", "#"); 77 | addTokenType(HEADER, "HEADER"); 78 | addTokenType(IDENTIFIER, "IDENTIFIER"); 79 | addTokenType(INC, "INC", "++"); 80 | addTokenType(NUMBER, "NUMBER"); 81 | addTokenType(LAND, "LAND", "&&"); 82 | addTokenType(LAND_EQ, "LAND_EQ", "&&="); 83 | addTokenType(LE, "LE", "<="); 84 | addTokenType(LITERAL, "LITERAL"); 85 | addTokenType(LOR, "LOR", "||"); 86 | addTokenType(LOR_EQ, "LOR_EQ", "||="); 87 | addTokenType(LSH, "LSH", "<<"); 88 | addTokenType(LSH_EQ, "LSH_EQ", "<<="); 89 | addTokenType(MOD_EQ, "MOD_EQ", "%="); 90 | addTokenType(MULT_EQ, "MULT_EQ", "*="); 91 | addTokenType(NE, "NE", "!="); 92 | addTokenType(NL, "NL"); 93 | addTokenType(OR_EQ, "OR_EQ", "|="); 94 | addTokenType(PASTE, "PASTE", "##"); 95 | addTokenType(PLUS_EQ, "PLUS_EQ", "+="); 96 | addTokenType(RANGE, "RANGE", ".."); 97 | addTokenType(RSH, "RSH", ">>"); 98 | addTokenType(RSH_EQ, "RSH_EQ", ">>="); 99 | addTokenType(SQSTRING, "SQSTRING"); 100 | addTokenType(STRING, "STRING"); 101 | addTokenType(SUB_EQ, "SUB_EQ", "-="); 102 | addTokenType(WHITESPACE, "WHITESPACE"); 103 | addTokenType(XOR_EQ, "XOR_EQ", "^="); 104 | addTokenType(M_ARG, "M_ARG"); 105 | addTokenType(M_PASTE, "M_PASTE"); 106 | addTokenType(M_STRING, "M_STRING"); 107 | addTokenType(P_LINE, "P_LINE"); 108 | addTokenType(INVALID, "INVALID"); 109 | } 110 | 111 | private final String name; 112 | private final String text; 113 | 114 | /* pp */ TokenType(@Nonnull String name, @CheckForNull String text) { 115 | this.name = name; 116 | this.text = text; 117 | } 118 | 119 | @Nonnull 120 | public String getName() { 121 | return name; 122 | } 123 | 124 | @CheckForNull 125 | public String getText() { 126 | return text; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/VirtualFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import java.io.IOException; 20 | import javax.annotation.CheckForNull; 21 | import javax.annotation.Nonnull; 22 | 23 | /** 24 | * An extremely lightweight virtual file interface. 25 | */ 26 | public interface VirtualFile { 27 | 28 | // public String getParent(); 29 | public boolean isFile(); 30 | 31 | @Nonnull 32 | public String getPath(); 33 | 34 | @Nonnull 35 | public String getName(); 36 | 37 | @CheckForNull 38 | public VirtualFile getParentFile(); 39 | 40 | @Nonnull 41 | public VirtualFile getChildFile(String name); 42 | 43 | @Nonnull 44 | public Source getSource() throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/VirtualFileSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | import javax.annotation.Nonnull; 20 | 21 | /** 22 | * An extremely lightweight virtual file system interface. 23 | */ 24 | public interface VirtualFileSystem { 25 | 26 | @Nonnull 27 | public VirtualFile getFile(@Nonnull String path); 28 | 29 | @Nonnull 30 | public VirtualFile getFile(@Nonnull String dir, @Nonnull String name); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/anarres/cpp/Warning.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2015, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | package org.anarres.cpp; 18 | 19 | /** 20 | * Warning classes which may optionally be emitted by the Preprocessor. 21 | */ 22 | public enum Warning { 23 | 24 | TRIGRAPHS, 25 | // TRADITIONAL, 26 | IMPORT, 27 | UNDEF, 28 | UNUSED_MACROS, 29 | ENDIF_LABELS, 30 | ERROR, 31 | // SYSTEM_HEADERS 32 | } 33 | -------------------------------------------------------------------------------- /src/main/velocity/org/anarres/cpp/Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Anarres C Preprocessor 3 | * Copyright (c) 2007-2008, Shevek 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | * or implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | */ 17 | 18 | package org.anarres.cpp; 19 | 20 | import javax.annotation.CheckForNull; 21 | import javax.annotation.Nonnull; 22 | 23 | /** 24 | * System version metadata for Anarres Java C Preprocessor ${version}. 25 | * 26 | * This class contains a main() and may be run to print the version. 27 | */ 28 | public class Version { 29 | 30 | /* Don't instantiate me */ 31 | private Version() { 32 | } 33 | 34 | private static final String VERSION = "${version}"; 35 | 36 | private static final int major; 37 | private static final int minor; 38 | private static final int patch; 39 | private static final String modifier; 40 | 41 | static { 42 | String[] tmp = VERSION.split("[\\.-]"); 43 | major = Integer.parseInt(tmp[0]); 44 | minor = Integer.parseInt(tmp[1]); 45 | patch = Integer.parseInt(tmp[2]); 46 | modifier = (tmp.length > 3) ? tmp[3] : null; 47 | } 48 | 49 | @Nonnull 50 | public static String getVersion() { 51 | return VERSION; 52 | } 53 | 54 | public static int getMajor() { 55 | return major; 56 | } 57 | 58 | public static int getMinor() { 59 | return minor; 60 | } 61 | 62 | public static int getPatch() { 63 | return patch; 64 | } 65 | 66 | @CheckForNull 67 | public static String getModifier() { 68 | return modifier; 69 | } 70 | 71 | public static boolean isSnapshot() { 72 | return "SNAPSHOT".equalsIgnoreCase(getModifier()); 73 | } 74 | 75 | public static void main(String[] args) { 76 | System.out.println("Version " + VERSION); 77 | System.out.println("getVersion() returns " + getVersion()); 78 | System.out.println("getMajor() returns " + getMajor()); 79 | System.out.println("getMinor() returns " + getMinor()); 80 | System.out.println("getPatch() returns " + getPatch()); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/scripts/jcpp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CPP_JAR=anarres-cpp.jar 4 | 5 | if [ -n "$CPP_ROOT" ] ; then 6 | CPP_ROOT="$CPP_ROOT" 7 | elif [ -f lib/$CPP_JAR ] ; then 8 | CPP_ROOT="." 9 | elif [ -f ../lib/$CPP_JAR ] ; then 10 | CPP_ROOT=".." 11 | elif [ -f $(dirname $0)/lib/$CPP_JAR ] ; then 12 | CPP_ROOT=$(dirname $0) 13 | else 14 | echo "Could not find $CPP_JAR. Please set CPP_ROOT." 15 | exit 1 16 | fi 17 | 18 | if [ -z "$CPP_LIB" ] ; then 19 | CPP_LIB=$CPP_ROOT/lib 20 | fi 21 | 22 | if [ -z "$CPP_CLASSPATH" ] ; then 23 | CPP_CLASSPATH="$(ls $CPP_LIB/*.jar | tr '\n' ':')" 24 | fi 25 | 26 | if [ -z "$CPP_MAINCLASS" ] ; then 27 | CPP_MAINCLASS=org.anarres.cpp.Main 28 | fi 29 | 30 | CPP_JFLAGS="-Xmx128M" 31 | 32 | exec java $CPP_JFLAGS -cp "$CPP_CLASSPATH" $CPP_MAINCLASS "$@" 33 | -------------------------------------------------------------------------------- /src/scripts/release.sh: -------------------------------------------------------------------------------- 1 | rsync -avP build/dist/anarres-cpp-*.tar.gz shevek@pink.anarres.org:public_html/projects/jcpp/ 2 | rsync -avP --exclude=.svn --exclude=autohandler build/javadoc shevek@pink.anarres.org:public_html/projects/jcpp/ 3 | cp build/tar/lib/anarres-cpp.jar /home/shevek/java/iengine/trunk/lib/runtime/jcpp/ 4 | cp build/tar/lib/anarres-cpp.jar /home/shevek/java/karma/trunk/lib/dp/ 5 | cp build/tar/lib/anarres-cpp.jar /home/shevek/java/dp/trunk/lib/runtime/cpp/ 6 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/BuildMetadataTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.io.Resources; 5 | import java.net.URL; 6 | import org.junit.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * 12 | * @author shevek 13 | */ 14 | public class BuildMetadataTest { 15 | 16 | private static final Logger LOG = LoggerFactory.getLogger(BuildMetadataTest.class); 17 | 18 | @Test 19 | public void testProperties() throws Exception { 20 | URL url = Resources.getResource("META-INF/jcpp.properties"); 21 | String text = Resources.asCharSource(url, Charsets.ISO_8859_1).read(); 22 | LOG.info("Metadata is " + text); 23 | } 24 | 25 | @Test 26 | public void testMetadata() throws Exception { 27 | BuildMetadata metadata = BuildMetadata.getInstance(); 28 | LOG.info("Version is " + metadata.getVersion()); 29 | LOG.info("BuildDate is " + metadata.getBuildDate()); 30 | LOG.info("ChangeId is " + metadata.getChangeId()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/CppReaderTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.StringReader; 5 | import java.util.Collections; 6 | import javax.annotation.Nonnull; 7 | import org.junit.Test; 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class CppReaderTest { 11 | 12 | public static String testCppReader(@Nonnull String in, Feature... f) 13 | throws Exception { 14 | System.out.println("Testing " + in); 15 | StringReader r = new StringReader(in); 16 | CppReader p = new CppReader(r); 17 | p.getPreprocessor().setSystemIncludePath( 18 | Collections.singletonList("src/test/resources") 19 | ); 20 | p.getPreprocessor().addFeatures(f); 21 | BufferedReader b = new BufferedReader(p); 22 | 23 | StringBuilder out = new StringBuilder(); 24 | String line; 25 | while ((line = b.readLine()) != null) { 26 | System.out.println(" >> " + line); 27 | out.append(line).append("\n"); 28 | } 29 | 30 | return out.toString(); 31 | } 32 | 33 | @Test 34 | public void testCppReader() 35 | throws Exception { 36 | testCppReader("#include \n", Feature.LINEMARKERS); 37 | } 38 | 39 | @Test 40 | public void testVarargs() 41 | throws Exception { 42 | // The newlines are irrelevant, We want exactly one "foo" 43 | testCppReader("#include \n"); 44 | } 45 | 46 | @Test 47 | public void testPragmaOnce() 48 | throws Exception { 49 | // The newlines are irrelevant, We want exactly one "foo" 50 | String out = testCppReader("#include \n", Feature.PRAGMA_ONCE); 51 | assertEquals("foo", out.trim()); 52 | } 53 | 54 | @Test 55 | public void testPragmaOnceWithMarkers() 56 | throws Exception { 57 | // The newlines are irrelevant, We want exactly one "foo" 58 | testCppReader("#include \n", Feature.PRAGMA_ONCE, Feature.LINEMARKERS); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/ErrorTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.IOException; 4 | import org.junit.Test; 5 | import static org.anarres.cpp.Token.*; 6 | import static org.junit.Assert.*; 7 | 8 | public class ErrorTest { 9 | 10 | private boolean testError(Preprocessor p) 11 | throws LexerException, 12 | IOException { 13 | for (;;) { 14 | Token tok = p.token(); 15 | if (tok.getType() == EOF) 16 | break; 17 | if (tok.getType() == INVALID) 18 | return true; 19 | } 20 | return false; 21 | } 22 | 23 | private void testError(String input) throws Exception { 24 | StringLexerSource sl; 25 | DefaultPreprocessorListener pl; 26 | Preprocessor p; 27 | 28 | /* Without a PreprocessorListener, throws an exception. */ 29 | sl = new StringLexerSource(input, true); 30 | p = new Preprocessor(); 31 | p.addFeature(Feature.CSYNTAX); 32 | p.addInput(sl); 33 | try { 34 | assertTrue(testError(p)); 35 | fail("Lexing unexpectedly succeeded without listener."); 36 | } catch (LexerException e) { 37 | /* required */ 38 | } 39 | 40 | /* With a PreprocessorListener, records the error. */ 41 | sl = new StringLexerSource(input, true); 42 | p = new Preprocessor(); 43 | p.addFeature(Feature.CSYNTAX); 44 | p.addInput(sl); 45 | pl = new DefaultPreprocessorListener(); 46 | p.setListener(pl); 47 | assertNotNull("CPP has listener", p.getListener()); 48 | assertTrue(testError(p)); 49 | assertTrue("Listener has errors", pl.getErrors() > 0); 50 | 51 | /* Without CSYNTAX, works happily. */ 52 | sl = new StringLexerSource(input, true); 53 | p = new Preprocessor(); 54 | p.addInput(sl); 55 | assertTrue(testError(p)); 56 | } 57 | 58 | @Test 59 | public void testErrors() throws Exception { 60 | testError("\""); 61 | testError("'"); 62 | // testError("''"); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/IncludeAbsoluteTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import com.google.common.io.CharStreams; 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.Reader; 7 | import org.junit.Test; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * 14 | * @author shevek 15 | */ 16 | public class IncludeAbsoluteTest { 17 | 18 | private static final Logger LOG = LoggerFactory.getLogger(IncludeAbsoluteTest.class); 19 | 20 | @Test 21 | public void testAbsoluteInclude() throws Exception { 22 | File file = new File("build/resources/test/absolute.h"); 23 | assertTrue(file.exists()); 24 | 25 | String input = "#include <" + file.getAbsolutePath() + ">\n"; 26 | LOG.info("Input: " + input); 27 | Preprocessor pp = new Preprocessor(); 28 | pp.addInput(new StringLexerSource(input, true)); 29 | Reader r = new CppReader(pp); 30 | String output = CharStreams.toString(r); 31 | r.close(); 32 | LOG.info("Output: " + output); 33 | assertTrue(output.contains("absolute-result")); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/JavaFileSystemTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.FileNotFoundException; 4 | import org.junit.Test; 5 | import static org.junit.Assert.assertFalse; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | public class JavaFileSystemTest { 9 | 10 | @Test 11 | public void testJavaFileSystem() throws Exception { 12 | JavaFileSystem fs = new JavaFileSystem(); 13 | VirtualFile f; 14 | 15 | /* Anyone who has this file on their Unix box is messed up. */ 16 | f = fs.getFile("/foo/bar baz"); 17 | try { 18 | f.getSource(); /* drop on floor */ 19 | 20 | assertTrue("Got a source for a non-file", f.isFile()); 21 | } catch (FileNotFoundException e) { 22 | assertFalse("Got no source for a file", f.isFile()); 23 | } 24 | 25 | /* We hope we have this. */ 26 | f = fs.getFile("/usr/include/stdio.h"); 27 | try { 28 | f.getSource(); /* drop on floor */ 29 | 30 | System.out.println("Opened stdio.h"); 31 | assertTrue("Got a source for a non-file", f.isFile()); 32 | } catch (FileNotFoundException e) { 33 | System.out.println("Failed to open stdio.h"); 34 | assertFalse("Got no source for a file", f.isFile()); 35 | } 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/JoinReaderTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.StringReader; 4 | import org.junit.Test; 5 | import static org.junit.Assert.assertEquals; 6 | 7 | public class JoinReaderTest { 8 | 9 | private void testJoinReader(String in, String out, boolean tg) 10 | throws Exception { 11 | System.out.println("Testing " + in + " => " + out); 12 | StringReader r = new StringReader(in); 13 | JoinReader j = new JoinReader(r, tg); 14 | 15 | for (int i = 0; i < out.length(); i++) { 16 | int c = j.read(); 17 | System.out.println("At offset " + i + ": " + (char) c); 18 | assertEquals(out.charAt(i), c); 19 | } 20 | assertEquals(-1, j.read()); 21 | assertEquals(-1, j.read()); 22 | } 23 | 24 | private void testJoinReader(String in, String out) 25 | throws Exception { 26 | testJoinReader(in, out, true); 27 | testJoinReader(in, out, false); 28 | } 29 | 30 | @Test 31 | public void testJoinReader() 32 | throws Exception { 33 | testJoinReader("ab", "ab"); 34 | testJoinReader("a\\b", "a\\b"); 35 | testJoinReader("a\nb", "a\nb"); 36 | testJoinReader("a\\\nb", "ab\n"); 37 | testJoinReader("foo??(bar", "foo[bar", true); 38 | testJoinReader("foo??/\nbar", "foobar\n", true); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/LexerSourceTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.util.Arrays; 4 | import org.junit.Test; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import static org.anarres.cpp.PreprocessorTest.assertType; 8 | import static org.anarres.cpp.Token.*; 9 | import static org.junit.Assert.*; 10 | 11 | public class LexerSourceTest { 12 | 13 | private static final Logger LOG = LoggerFactory.getLogger(LexerSourceTest.class); 14 | 15 | public static void testLexerSource(String in, boolean textmatch, int... out) 16 | throws Exception { 17 | LOG.info("Testing '" + in + "' => " 18 | + Arrays.toString(out)); 19 | StringLexerSource s = new StringLexerSource(in); 20 | 21 | StringBuilder buf = new StringBuilder(); 22 | for (int i = 0; i < out.length; i++) { 23 | Token tok = s.token(); 24 | LOG.info("Token is " + tok); 25 | assertType(out[i], tok); 26 | // assertEquals(col, tok.getColumn()); 27 | buf.append(tok.getText()); 28 | } 29 | 30 | Token tok = s.token(); 31 | LOG.info("Token is " + tok); 32 | assertType(EOF, tok); 33 | 34 | if (textmatch) 35 | assertEquals(in, buf.toString()); 36 | } 37 | 38 | @Test 39 | public void testLexerSource() 40 | throws Exception { 41 | 42 | testLexerSource("int a = 5;", true, 43 | IDENTIFIER, WHITESPACE, IDENTIFIER, WHITESPACE, 44 | '=', WHITESPACE, NUMBER, ';' 45 | ); 46 | 47 | // \n is WHITESPACE because ppvalid = false 48 | testLexerSource("# # \r\n\n\r \rfoo", true, 49 | HASH, WHITESPACE, '#', WHITESPACE, IDENTIFIER 50 | ); 51 | 52 | // No match - trigraphs 53 | testLexerSource("%:%:", false, PASTE); 54 | testLexerSource("%:?", false, '#', '?'); 55 | testLexerSource("%:%=", false, '#', MOD_EQ); 56 | 57 | testLexerSource("0x1234ffdUL 0765I", true, 58 | NUMBER, WHITESPACE, NUMBER); 59 | 60 | testLexerSource("+= -= *= /= %= <= >= >>= <<= &= |= ^= x", true, 61 | PLUS_EQ, WHITESPACE, 62 | SUB_EQ, WHITESPACE, 63 | MULT_EQ, WHITESPACE, 64 | DIV_EQ, WHITESPACE, 65 | MOD_EQ, WHITESPACE, 66 | LE, WHITESPACE, 67 | GE, WHITESPACE, 68 | RSH_EQ, WHITESPACE, 69 | LSH_EQ, WHITESPACE, 70 | AND_EQ, WHITESPACE, 71 | OR_EQ, WHITESPACE, 72 | XOR_EQ, WHITESPACE, 73 | IDENTIFIER); 74 | 75 | testLexerSource("/**/", true, CCOMMENT); 76 | testLexerSource("/* /**/ */", true, CCOMMENT, WHITESPACE, '*', '/'); 77 | testLexerSource("/** ** **/", true, CCOMMENT); 78 | testLexerSource("//* ** **/", true, CPPCOMMENT); 79 | testLexerSource("'\\r' '\\xf' '\\xff' 'x' 'aa' ''", true, 80 | CHARACTER, WHITESPACE, 81 | CHARACTER, WHITESPACE, 82 | CHARACTER, WHITESPACE, 83 | CHARACTER, WHITESPACE, 84 | SQSTRING, WHITESPACE, 85 | SQSTRING); 86 | 87 | if (false) // Actually, I think this is illegal. 88 | testLexerSource("1i1I1l1L1ui1ul", true, 89 | NUMBER, NUMBER, 90 | NUMBER, NUMBER, 91 | NUMBER, NUMBER); 92 | 93 | testLexerSource("'' 'x' 'xx'", true, 94 | SQSTRING, WHITESPACE, CHARACTER, WHITESPACE, SQSTRING); 95 | } 96 | 97 | @Test 98 | public void testNumbers() throws Exception { 99 | testLexerSource("0", true, NUMBER); 100 | testLexerSource("045", true, NUMBER); 101 | testLexerSource("45", true, NUMBER); 102 | testLexerSource("0.45", true, NUMBER); 103 | testLexerSource("1.45", true, NUMBER); 104 | testLexerSource("1e6", true, NUMBER); 105 | testLexerSource("1.45e6", true, NUMBER); 106 | testLexerSource(".45e6", true, NUMBER); 107 | testLexerSource("-6", true, '-', NUMBER); 108 | } 109 | 110 | @Test 111 | public void testNumbersSuffix() throws Exception { 112 | testLexerSource("6f", true, NUMBER); 113 | testLexerSource("6d", true, NUMBER); 114 | testLexerSource("6l", true, NUMBER); 115 | testLexerSource("6ll", true, NUMBER); 116 | testLexerSource("6ul", true, NUMBER); 117 | testLexerSource("6ull", true, NUMBER); 118 | testLexerSource("6e3f", true, NUMBER); 119 | testLexerSource("6e3d", true, NUMBER); 120 | testLexerSource("6e3l", true, NUMBER); 121 | testLexerSource("6e3ll", true, NUMBER); 122 | testLexerSource("6e3ul", true, NUMBER); 123 | testLexerSource("6e3ull", true, NUMBER); 124 | } 125 | 126 | @Test 127 | public void testNumbersInvalid() throws Exception { 128 | // testLexerSource("0x foo", true, INVALID, WHITESPACE, IDENTIFIER); // FAIL 129 | testLexerSource("6x foo", true, INVALID, WHITESPACE, IDENTIFIER); 130 | testLexerSource("6g foo", true, INVALID, WHITESPACE, IDENTIFIER); 131 | testLexerSource("6xsd foo", true, INVALID, WHITESPACE, IDENTIFIER); 132 | testLexerSource("6gsd foo", true, INVALID, WHITESPACE, IDENTIFIER); 133 | } 134 | 135 | @Test 136 | public void testUnterminatedComment() throws Exception { 137 | testLexerSource("5 /*", false, NUMBER, WHITESPACE, INVALID); // Bug #15 138 | testLexerSource("5 //", false, NUMBER, WHITESPACE, CPPCOMMENT); 139 | } 140 | 141 | @Test 142 | public void testUnicode()throws Exception{ 143 | testLexerSource("foo \u2018bar\u2019 baz", true, IDENTIFIER, WHITESPACE, 8216, IDENTIFIER, 8217, WHITESPACE, IDENTIFIER); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/MainTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import org.junit.Test; 4 | 5 | public class MainTest { 6 | 7 | @Test 8 | public void testMain() throws Exception { 9 | Main.main(new String[]{"--version"}); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/NumericValueTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.IOException; 4 | import org.junit.Test; 5 | import static org.anarres.cpp.Token.*; 6 | import static org.junit.Assert.*; 7 | 8 | /** 9 | * 10 | * @author shevek 11 | */ 12 | public class NumericValueTest { 13 | 14 | private Token testNumericValue(String in) throws IOException, LexerException { 15 | StringLexerSource s = new StringLexerSource(in); 16 | 17 | Token tok = s.token(); 18 | System.out.println("Token is " + tok); 19 | assertEquals(NUMBER, tok.getType()); 20 | 21 | Token eof = s.token(); 22 | assertEquals("Didn't get EOF, but " + tok, EOF, eof.getType()); 23 | 24 | return tok; 25 | } 26 | 27 | private void testNumericValue(String in, double out) throws IOException, LexerException { 28 | System.out.println("Testing '" + in + "' -> " + out); 29 | Token tok = testNumericValue(in); 30 | assertEquals(in, tok.getText()); 31 | NumericValue value = (NumericValue) tok.getValue(); 32 | assertEquals("Double mismatch", out, value.doubleValue(), 0.01d); 33 | assertEquals("Float mismatch", (float) out, value.floatValue(), 0.01f); 34 | assertEquals("Long mismatch", (long) out, value.longValue()); 35 | assertEquals("Integer mismatch", (int) out, value.intValue()); 36 | } 37 | 38 | @Test 39 | public void testNumericValue() throws Exception { 40 | 41 | // Zero 42 | testNumericValue("0", 0); 43 | 44 | // Decimal 45 | testNumericValue("1", 1); 46 | testNumericValue("1L", 1); 47 | testNumericValue("12", 12); 48 | testNumericValue("12L", 12); 49 | 50 | // Hex 51 | testNumericValue("0xf", 0xf); 52 | testNumericValue("0xfL", 0xf); 53 | testNumericValue("0x12", 0x12); 54 | testNumericValue("0x12L", 0x12); 55 | 56 | // Negative 57 | // testNumericValue("-0", 0); 58 | // testNumericValue("-1", -1); 59 | // Negative hex 60 | // testNumericValue("-0x56", -0x56); 61 | // testNumericValue("-0x102", -0x102); 62 | // Octal and negative octal 63 | testNumericValue("0673", Integer.parseInt("673", 8)); 64 | // testNumericValue("-0673", Integer.parseInt("-673", 8)); 65 | 66 | // Floating point 67 | testNumericValue(".0", 0); 68 | testNumericValue(".00", 0); 69 | testNumericValue("0.", 0); 70 | testNumericValue("0.0", 0); 71 | testNumericValue("00.0", 0); 72 | testNumericValue("00.", 0); 73 | 74 | // Sign on exponents 75 | testNumericValue("1e1", 1e1); 76 | // testNumericValue("-1e1", -1e1); 77 | testNumericValue("1e-1", 1e-1); 78 | testNumericValue("1e+1", 1e+1); 79 | 80 | // Hex numbers with decimal exponents 81 | testNumericValue("0x12e3", 0x12e3); 82 | testNumericValue("0x12p3", 0x12p3); 83 | 84 | // Octal numbers with decimal exponents 85 | testNumericValue("012e3", 012e3); // Fails 86 | testNumericValue("067e4", 067e4); // Fails 87 | 88 | // Issues a warning. 89 | try { 90 | testNumericValue("097", 97); 91 | fail("No warning."); 92 | } catch (LexerException e) { 93 | } 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/PragmaTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.anarres.cpp; 7 | 8 | import com.google.common.base.Charsets; 9 | import com.google.common.io.CharSource; 10 | import com.google.common.io.CharStreams; 11 | import com.google.common.io.Files; 12 | import java.io.File; 13 | import org.junit.Test; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import static org.junit.Assert.*; 17 | 18 | /** 19 | * 20 | * @author shevek 21 | */ 22 | public class PragmaTest { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(PragmaTest.class); 25 | 26 | @Test 27 | public void testPragma() throws Exception { 28 | File file = new File("build/resources/test/pragma.c"); 29 | assertTrue(file.exists()); 30 | 31 | CharSource source = Files.asCharSource(file, Charsets.UTF_8); 32 | CppReader r = new CppReader(source.openBufferedStream()); 33 | r.getPreprocessor().setListener(new DefaultPreprocessorListener()); 34 | String output = CharStreams.toString(r); 35 | r.close(); 36 | LOG.info("Output: " + output); 37 | // assertTrue(output.contains("absolute-result")); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/PreprocessorTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import java.io.InputStreamReader; 4 | import java.io.OutputStreamWriter; 5 | import java.io.PipedInputStream; 6 | import java.io.PipedOutputStream; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import static org.anarres.cpp.Token.*; 12 | import static org.junit.Assert.*; 13 | 14 | public class PreprocessorTest { 15 | 16 | private static final Logger LOG = LoggerFactory.getLogger(PreprocessorTest.class); 17 | 18 | private OutputStreamWriter writer; 19 | private Preprocessor p; 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | final PipedOutputStream po = new PipedOutputStream(); 24 | writer = new OutputStreamWriter(po); 25 | 26 | p = new Preprocessor(); 27 | p.addInput( 28 | new LexerSource( 29 | new InputStreamReader( 30 | new PipedInputStream(po) 31 | ), 32 | true 33 | ) 34 | ); 35 | } 36 | 37 | private static class I { 38 | 39 | private final String t; 40 | 41 | public I(String t) { 42 | this.t = t; 43 | } 44 | 45 | public String getText() { 46 | return t; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return getText(); 52 | } 53 | } 54 | 55 | private static I I(String t) { 56 | return new I(t); 57 | } 58 | 59 | /* 60 | * When writing tests in this file, remember the preprocessor 61 | * stashes NLs, so you won't see an immediate NL at the end of any 62 | * input line. You will see it right before the next nonblank on 63 | * the following input line. 64 | */ 65 | @Test 66 | public void testPreprocessor() throws Exception { 67 | /* Magic macros */ 68 | testInput("line = __LINE__\n", 69 | I("line"), WHITESPACE, '=', WHITESPACE, NUMBER 70 | /*, NL - all nls deferred so as not to block the reader */ 71 | ); 72 | testInput("file = __FILE__\n", NL, /* from before, etc */ 73 | I("file"), WHITESPACE, '=', WHITESPACE, STRING 74 | ); 75 | 76 | /* Simple definitions */ 77 | testInput("#define A a /* a defined */\n", NL); 78 | testInput("#define B b /* b defined */\n", NL); 79 | testInput("#define C c /* c defined */\n", NL); 80 | 81 | /* Expansion of arguments */ 82 | testInput("#define EXPAND(x) x\n", NL); 83 | testInput("EXPAND(a)\n", NL, I("a")); 84 | testInput("EXPAND(A)\n", NL, I("a")); 85 | 86 | /* Stringification */ 87 | testInput("#define _STRINGIFY(x) #x\n", NL); 88 | testInput("_STRINGIFY(A)\n", NL, "A"); 89 | testInput("#define STRINGIFY(x) _STRINGIFY(x)\n", NL); 90 | testInput("STRINGIFY(b)\n", NL, "b"); 91 | testInput("STRINGIFY(A)\n", NL, "a"); 92 | 93 | /* Concatenation */ 94 | testInput("#define _CONCAT(x, y) x ## y\n", NL); 95 | testInput("_CONCAT(A, B)\n", NL, I("AB")); 96 | testInput("#define A_CONCAT done_a_concat\n", NL); 97 | testInput("_CONCAT(A, _CONCAT(B, C))\n", NL, 98 | I("done_a_concat"), '(', I("b"), ',', WHITESPACE, I("c"), ')' 99 | ); 100 | testInput("#define CONCAT(x, y) _CONCAT(x, y)\n", NL); 101 | testInput("CONCAT(A, CONCAT(B, C))\n", NL, I("abc")); 102 | testInput("#define _CONCAT3(x, y, z) x ## y ## z\n", NL); 103 | testInput("_CONCAT3(a, b, c)\n", NL, I("abc")); 104 | testInput("_CONCAT3(A, B, C)\n", NL, I("ABC")); 105 | testInput("_CONCAT(test_, inline)\n", NL, I("test_inline")); 106 | testInput("_CONCAT(test_, \nnewline)\n", NL, I("test_newline")); 107 | 108 | /* Redefinitions, undefinitions. */ 109 | testInput("#define two three\n", NL); 110 | testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT); 111 | testInput("#define one two\n", NL); 112 | testInput("one /* three */\n", NL, I("three"), WHITESPACE, CCOMMENT); 113 | testInput("#undef two\n", NL); 114 | testInput("#define two five\n", NL); 115 | testInput("one /* five */\n", NL, I("five"), WHITESPACE, CCOMMENT); 116 | testInput("#undef two\n", NL); 117 | testInput("one /* two */\n", NL, I("two"), WHITESPACE, CCOMMENT); 118 | testInput("#undef one\n", NL); 119 | testInput("#define one four\n", NL); 120 | testInput("one /* four */\n", NL, I("four"), WHITESPACE, CCOMMENT); 121 | testInput("#undef one\n", NL); 122 | testInput("#define one one\n", NL); 123 | testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT); 124 | 125 | /* Variadic macros. */ 126 | testInput("#define var(x...) a x __VA_ARGS__ b\n", NL); 127 | testInput("var(e, f, g)\n", NL, 128 | I("a"), WHITESPACE, 129 | I("e"), ',', WHITESPACE, 130 | I("f"), ',', WHITESPACE, 131 | I("g"), WHITESPACE, 132 | I("__VA_ARGS__"), WHITESPACE, // __VA_ARGS__ is not expanded in this case. 133 | I("b") 134 | ); 135 | /* Missing arguments are fine. */ 136 | testInput("var()\n", NL, 137 | I("a"), WHITESPACE, 138 | /* No expansion for 'x'. */ WHITESPACE, 139 | I("__VA_ARGS__"), WHITESPACE, 140 | I("b") 141 | ); 142 | 143 | /* Variadic macros with anonymous args. */ 144 | testInput("#define var2(x, ...) a x __VA_ARGS__ e\n", NL); 145 | testInput("var2(b, c, d)\n", NL, 146 | I("a"), WHITESPACE, 147 | I("b"), WHITESPACE, 148 | I("c"), ',', WHITESPACE, 149 | I("d"), WHITESPACE, 150 | I("e") 151 | ); 152 | /* Missing arguments are fine. */ 153 | testInput("var2(b)\n", NL, 154 | I("a"), WHITESPACE, 155 | I("b"), WHITESPACE, 156 | /* No expansion for '__VA_ARGS__'. */ WHITESPACE, 157 | I("e") 158 | ); 159 | 160 | testInput("#define var3(...) a __VA_ARGS__ d\n", NL); 161 | testInput("var3(b, c)\n", NL, 162 | I("a"), WHITESPACE, 163 | I("b"), ',', WHITESPACE, 164 | I("c"), WHITESPACE, 165 | I("d") 166 | ); 167 | testInput("var3()\n", NL, 168 | I("a"), WHITESPACE, 169 | /* No expansion for '__VA_ARGS__'. */ WHITESPACE, 170 | I("d") 171 | ); 172 | 173 | testInput("#define _Widen(x) L ## x\n", NL); 174 | testInput("#define Widen(x) _Widen(x)\n", NL); 175 | testInput("#define LStr(x) _Widen(#x)\n", NL); 176 | testInput("LStr(x);\n", NL, I("L"), "x", ';'); 177 | 178 | testInput("'foo'\n", NL, SQSTRING); 179 | testInput("#if 1 ? 2 : 0\nTEXT\n#endif\n", NL, NL, I("TEXT"), NL); 180 | testInput("#if 1 ? 0 : 2\nTEXT\n#endif\n", NL, NL, NL); 181 | testInput("#if 0 ? 0 : 2\nTEXT\n#endif\n", NL, NL, I("TEXT"), NL); 182 | testInput("#if 0 ? 2 : 0\nTEXT\n#endif\n", NL, NL, NL); 183 | 184 | writer.close(); 185 | 186 | Token t; 187 | do { 188 | t = p.token(); 189 | LOG.warn("Remaining token " + t); 190 | } while (t.getType() != EOF); 191 | } 192 | 193 | @Test 194 | public void testPreprocessorUnterminated() throws Exception { 195 | testInput("#ifndef X\na\n#else\nb\n"); // Bug #16 196 | 197 | writer.close(); 198 | 199 | Token t; 200 | do { 201 | t = p.token(); 202 | LOG.warn("Remaining token " + t); 203 | } while (t.getType() != EOF); 204 | } 205 | 206 | public static void assertType(int type, Token t) { 207 | String typeExpect = TokenType.getTokenName(type); 208 | String typeActual = TokenType.getTokenName(t.getType()); 209 | assertEquals("Expected " + typeExpect + " but got " + typeActual, type, t.getType()); 210 | } 211 | 212 | private void testInput(String in, Object... out) 213 | throws Exception { 214 | LOG.info("Input: " + in); 215 | writer.write(in); 216 | writer.flush(); 217 | for (Object v : out) { 218 | Token t = p.token(); 219 | LOG.info(String.valueOf(t)); 220 | if (v instanceof String) { 221 | if (t.getType() != STRING) 222 | fail("Expected STRING, but got " + t); 223 | assertEquals(v, t.getValue()); 224 | } else if (v instanceof I) { 225 | if (t.getType() != IDENTIFIER) 226 | fail("Expected IDENTIFIER " + v + ", but got " + t); 227 | assertEquals(((I) v).getText(), t.getText()); 228 | } else if (v instanceof Character) { 229 | assertType(((Character) v).charValue(), t); 230 | } else if (v instanceof Integer) { 231 | assertType(((Number) v).intValue(), t); 232 | } else { 233 | fail("Bad object " + v.getClass()); 234 | } 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/RegressionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.anarres.cpp; 7 | 8 | import com.google.common.base.Charsets; 9 | import com.google.common.io.CharStreams; 10 | import com.google.common.io.Files; 11 | import com.google.common.io.PatternFilenameFilter; 12 | import java.io.File; 13 | import java.io.StringReader; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | import org.junit.runners.Parameterized; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import static org.junit.Assert.assertEquals; 22 | 23 | /** 24 | * 25 | * @author shevek 26 | */ 27 | @RunWith(Parameterized.class) 28 | public class RegressionTest { 29 | 30 | private static final Logger LOG = LoggerFactory.getLogger(RegressionTest.class); 31 | 32 | @Parameterized.Parameters(name = "{0}") 33 | public static List data() throws Exception { 34 | List out = new ArrayList(); 35 | 36 | File dir = new File("build/resources/test/regression"); 37 | for (File inFile : dir.listFiles(new PatternFilenameFilter(".*\\.in"))) { 38 | String name = Files.getNameWithoutExtension(inFile.getName()); 39 | File outFile = new File(dir, name + ".out"); 40 | out.add(new Object[]{name, inFile, outFile}); 41 | } 42 | 43 | return out; 44 | } 45 | 46 | private final String name; 47 | private final File inFile; 48 | private final File outFile; 49 | 50 | public RegressionTest(String name, File inFile, File outFile) { 51 | this.name = name; 52 | this.inFile = inFile; 53 | this.outFile = outFile; 54 | } 55 | 56 | @Test 57 | public void testRegression() throws Exception { 58 | String inText = Files.toString(inFile, Charsets.UTF_8); 59 | LOG.info("Read " + name + ":\n" + inText); 60 | CppReader cppReader = new CppReader(new StringReader(inText)); 61 | String cppText = CharStreams.toString(cppReader); 62 | LOG.info("Generated " + name + ":\n" + cppText); 63 | if (outFile.exists()) { 64 | String outText = Files.toString(outFile, Charsets.UTF_8); 65 | LOG.info("Expected " + name + ":\n" + outText); 66 | assertEquals(outText, inText); 67 | } 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/TokenPastingWhitespaceTest.java: -------------------------------------------------------------------------------- 1 | package org.anarres.cpp; 2 | 3 | import com.google.common.io.CharStreams; 4 | import java.io.IOException; 5 | import java.io.Reader; 6 | import org.junit.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import static org.junit.Assert.*; 10 | 11 | /** 12 | * https://github.com/shevek/jcpp/issues/25 13 | * 14 | * @author shevek 15 | */ 16 | public class TokenPastingWhitespaceTest { 17 | 18 | private static final Logger LOG = LoggerFactory.getLogger(TokenPastingWhitespaceTest.class); 19 | 20 | @Test 21 | public void testWhitespacePasting() throws IOException { 22 | Preprocessor pp = new Preprocessor(); 23 | pp.addInput(new StringLexerSource( 24 | "#define ONE(arg) one_##arg\n" 25 | + "#define TWO(arg) ONE(two_##arg)\n" 26 | + "\n" 27 | + "TWO(good)\n" 28 | + "TWO( /* evil newline */\n" 29 | + " bad)\n" 30 | + "\n" 31 | + "ONE(good)\n" 32 | + "ONE( /* evil newline */\n" 33 | + " bad)\n", true)); 34 | Reader r = new CppReader(pp); 35 | String text = CharStreams.toString(r).trim(); 36 | LOG.info("Output is:\n" + text); 37 | assertEquals("one_two_good\n" 38 | + "one_two_bad\n" 39 | + "\n" 40 | + "one_good\n" 41 | + "one_bad", text); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/anarres/cpp/VaArgsPastingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.anarres.cpp; 7 | 8 | import com.google.common.io.CharStreams; 9 | import java.io.IOException; 10 | import java.io.Reader; 11 | import org.junit.Test; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * 18 | * @author shevek 19 | */ 20 | public class VaArgsPastingTest { 21 | 22 | private static final Logger LOG = LoggerFactory.getLogger(VaArgsPastingTest.class); 23 | 24 | @Test 25 | public void testWhitespacePasting() throws IOException { 26 | String input 27 | = "#define REGULAR_ARGS(x, y) foo(x, y)\n" 28 | + "#define REGULAR_ELLIPSIS(x, y...) foo(x, y)\n" 29 | + "#define REGULAR_VAARGS(x, ...) foo(x, __VA_ARGS__)\n" 30 | + "#define PASTE_ARGS(x, y) foo(x, ## y)\n" 31 | + "#define PASTE_ELLIPSIS(x, y...) foo(x, ## y)\n" 32 | + "#define PASTE_VAARGS(x, ...) foo(x, ## __VA_ARGS__)\n" 33 | + "" 34 | + "REGULAR_ARGS(a, b) // REGULAR_ARGS 2\n" 35 | + "REGULAR_ELLIPSIS(a, b) // REGULAR_ELLIPSIS 2\n" 36 | + "REGULAR_ELLIPSIS(a) // REGULAR_ELLIPSIS 1\n" 37 | + "REGULAR_VAARGS(a, b) // REGULAR_VAARGS 2\n" 38 | + "REGULAR_VAARGS(a) // REGULAR_VAARGS 1\n" 39 | + "" 40 | + "PASTE_ARGS(a, b) // PASTE_ARGS 2\n" 41 | + "PASTE_ELLIPSIS(a, b) // PASTE_ELLIPSIS 2\n" 42 | + "PASTE_ELLIPSIS(a) // PASTE_ELLIPSIS 1\n" 43 | + "PASTE_VAARGS(a, b) // PASTE_VAARGS 2\n" 44 | + "PASTE_VAARGS(a) // PASTE_VAARGS 1\n"; 45 | LOG.info("Input is:\n" + input); 46 | Preprocessor pp = new Preprocessor(); 47 | pp.addFeature(Feature.KEEPCOMMENTS); 48 | pp.addInput(new StringLexerSource(input, true)); 49 | Reader r = new CppReader(pp); 50 | String output = CharStreams.toString(r).trim(); 51 | LOG.info("Output is:\n" + output); 52 | assertEquals("foo(a, b) // REGULAR_ARGS 2\n" 53 | + "foo(a, b) // REGULAR_ELLIPSIS 2\n" 54 | + "foo(a, ) // REGULAR_ELLIPSIS 1\n" 55 | + "foo(a, b) // REGULAR_VAARGS 2\n" 56 | + "foo(a, ) // REGULAR_VAARGS 1\n" 57 | + "foo(a,b) // PASTE_ARGS 2\n" // cpp outputs a warning and a space after the comma, similar below. 58 | + "foo(a,b) // PASTE_ELLIPSIS 2\n" 59 | + "foo(a) // PASTE_ELLIPSIS 1\n" 60 | + "foo(a,b) // PASTE_VAARGS 2\n" 61 | + "foo(a) // PASTE_VAARGS 1", output); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/resources/absolute.h: -------------------------------------------------------------------------------- 1 | absolute-result 2 | -------------------------------------------------------------------------------- /src/test/resources/lines.c: -------------------------------------------------------------------------------- 1 | lines-c-line-1 2 | #include 3 | lines-c-line-3 4 | 5 | lines-c-line-5 6 | 7 | #include 8 | 9 | lines-c-line-9 10 | -------------------------------------------------------------------------------- /src/test/resources/lines1.h: -------------------------------------------------------------------------------- 1 | 2 | lines1-h-line-2 3 | 4 | /* multi 5 | line 6 | comment 7 | */ 8 | 9 | #include 10 | lines1-h-line-10 11 | 12 | lines1-h-line-12 13 | #include 14 | 15 | /* trailing multiline comment 16 | with 17 | more lines 18 | */ 19 | -------------------------------------------------------------------------------- /src/test/resources/lines2.h: -------------------------------------------------------------------------------- 1 | 2 | lines2-h-line-2 3 | 4 | /* 5 | comment 6 | 7 | blank and multiline*/ 8 | 9 | lines2-h-line-9 10 | 11 | -------------------------------------------------------------------------------- /src/test/resources/once.c: -------------------------------------------------------------------------------- 1 | #include "once.h" 2 | #include "once.h" 3 | -------------------------------------------------------------------------------- /src/test/resources/once.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | foo 3 | -------------------------------------------------------------------------------- /src/test/resources/pragma.c: -------------------------------------------------------------------------------- 1 | 2 | #pragma 3 | #pragma once 4 | #pragma foo(bar) 5 | #pragma #pragma 6 | #pragma #include 7 | #pragma #include 8 | 9 | -------------------------------------------------------------------------------- /src/test/resources/regression/lex-char.in: -------------------------------------------------------------------------------- 1 | #define EXAMPLE_X 'a' 2 | 3 | #if EXAMPLE == EXAMPLE_X 4 | #endif 5 | -------------------------------------------------------------------------------- /src/test/resources/test0.c: -------------------------------------------------------------------------------- 1 | line = __LINE__ 2 | file = __FILE__ 3 | 4 | #define A a /* a defined */ 5 | #define B b /* b defined */ 6 | #define C c /* c defined */ 7 | 8 | #define EXPAND(x) x 9 | EXPAND(a) -> a 10 | EXPAND(A) -> a 11 | 12 | #define _STRINGIFY(x) #x 13 | _STRINGIFY(A) -> "A" 14 | 15 | #define STRINGIFY(x) _STRINGIFY(x) 16 | STRINGIFY(b) -> "b" 17 | STRINGIFY(A) -> "a" 18 | 19 | #define _CONCAT(x, y) x ## y 20 | _CONCAT(A, B) -> AB 21 | 22 | #define A_CONCAT done_a_concat 23 | _CONCAT(A, _CONCAT(B, C)) -> done_a_concat(b, c) 24 | 25 | #define CONCAT(x, y) _CONCAT(x, y) 26 | CONCAT(A, CONCAT(B, C)) -> abc 27 | 28 | #define _CONCAT3(x, y, z) x ## y ## z 29 | _CONCAT3(a, b, c) -> abc 30 | _CONCAT3(A, B, C) -> ABC 31 | _CONCAT3(A, EXPAND(B), C) -> AEXPAND(b)C 32 | 33 | Line is __LINE__ 34 | File is __FILE__ 35 | 36 | #define two three 37 | one /* one */ 38 | #define one two 39 | one /* three */ 40 | #undef two 41 | #define two five 42 | one /* five */ 43 | #undef two 44 | one /* two */ 45 | #undef one 46 | #define one four 47 | one /* four */ 48 | #undef one 49 | #define one one 50 | one /* one */ 51 | 52 | /* warning line 57 column 0 */ 53 | #warning arse 54 | 55 | #define foo(x) foo(x, b) 56 | foo(1) -> _foo(1, b) without the _ 57 | foo(foo(2)) -> _foo(_foo(2, b), b) without the _ 58 | // foo(y, z) 59 | 60 | #define var(x...) a x b 61 | var(e, f, g) -> a e, f, g b 62 | -------------------------------------------------------------------------------- /src/test/resources/test0.h: -------------------------------------------------------------------------------- 1 | 2 | test0start_2 3 | 4 | #include "test1.h" 5 | 6 | test0end___6 7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/test1.c: -------------------------------------------------------------------------------- 1 | #include "./test0.h" 2 | #include 3 | -------------------------------------------------------------------------------- /src/test/resources/test1.h: -------------------------------------------------------------------------------- 1 | 2 | test1start_2 3 | 4 | test1mid___4 5 | 6 | test1end___6 7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/trigraph.c: -------------------------------------------------------------------------------- 1 | ??/ 2 | -------------------------------------------------------------------------------- /src/test/resources/varargs.c: -------------------------------------------------------------------------------- 1 | #define var(x...) a x __VA_ARGS__ b 2 | var(b, c, d) 3 | 4 | #define var2(x, ...) a x __VA_ARGS__ b 5 | var2(b, c, d) 6 | 7 | #define var3(...) a x __VA_ARGS__ b 8 | var3(b, c, d) 9 | --------------------------------------------------------------------------------