diff --git a/.gitignore b/.gitignore index a8b6931..1ad0f80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ -build +/build +/target + +*~ .*.swp -.gradle + +.classpath +.project diff --git a/README b/README.md similarity index 80% rename from README rename to README.md index 0dead99..03b27ff 100644 --- a/README +++ b/README.md @@ -1,3 +1,5 @@ +# Introduction + The C Preprocessor is an interesting standard. It appears to be derived from the de-facto behaviour of the first preprocessors, and has evolved over the years. Implementation is therefore difficult. @@ -11,3 +13,8 @@ head examined). This project has has been used to successfully preprocess much of the source code of the GNU C library. As of version 1.2.5, it can also preprocess the Apple Objective C library. + +# Documentation + +* [JavaDoc API](http://shevek.github.io/jcpp/docs/javadoc/) +* [Coverage Report](http://shevek.github.io/jcpp/docs/cobertura/) diff --git a/build.gradle b/build.gradle deleted file mode 100644 index c99935d..0000000 --- a/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -// Establish version and status -ext.githubProjectName = rootProject.name // Change if github project name is not the same as the root project's name -group = "org.anarres" - -buildscript { - repositories { - // mavenLocal() - mavenCentral() // maven { url 'http://jcenter.bintray.com' } - } - apply from: file('gradle/buildscript.gradle'), to: buildscript -} - -allprojects { - repositories { - // mavenLocal() - mavenCentral() // maven { url: 'http://jcenter.bintray.com' } - } -} - -apply plugin: 'idea' - -apply from: file('gradle/convention.gradle') -apply from: file('gradle/maven.gradle') -apply from: file('gradle/check.gradle') -apply from: file('gradle/license.gradle') -// apply from: file('gradle/release.gradle') - -apply plugin: 'application' -apply plugin: VelocityPlugin - -dependencies { - compile 'com.google.code.findbugs:jsr305:2.0.2' - compile 'gnu.getopt:java-getopt:1.0.13' - compile 'org.apache.ant:ant:1.7.0' - - testCompile 'junit:junit:4.8.1' -} - -velocity { - def p = project - context { - version = p.version - } -} - -test { - systemProperty 'org.apache.commons.logging.Log', 'org.apache.commons.logging.impl.SimpleLog' - systemProperty 'org.apache.commons.logging.simplelog.defaultlog', 'debug' - - testLogging { - if (System.properties['test.single']) { - // events "passed", "skipped", "failed" - events "started", "passed", "skipped", "failed" - showExceptions true - exceptionFormat "full" - showStandardStreams true - } else { - events "failed" - } - - debug { - events "started", "passed", "skipped", "failed", "standard_out", "standard_error" - exceptionFormat "full" - } - } -} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index 324859d..0000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'groovy' -apply plugin: 'idea' - -repositories { - mavenCentral() -} - -dependencies { - compile gradleApi() - compile 'org.apache.velocity:velocity:1.7' -} - diff --git a/buildSrc/src/main/groovy/VelocityPlugin.groovy b/buildSrc/src/main/groovy/VelocityPlugin.groovy deleted file mode 100644 index cc8741a..0000000 --- a/buildSrc/src/main/groovy/VelocityPlugin.groovy +++ /dev/null @@ -1,33 +0,0 @@ -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.apache.velocity.VelocityContext -import org.apache.velocity.app.VelocityEngine -import org.apache.velocity.runtime.log.SystemLogChute - -class VelocityPluginExtension { - String inputDir = "src/main/velocity" - String outputDir = "build/generated-sources/velocity" - Map contextValues = [:] - def context(Closure closure) { - contextValues.with closure - } -} - -class VelocityPlugin implements Plugin { - void apply(Project project) { - - project.extensions.create("velocity", VelocityPluginExtension) - - project.task('velocityVpp', type: VelocityTask) { - description "Preprocesses velocity template files." - inputDir = project.file(project.velocity.inputDir) - outputDir = project.file(project.velocity.outputDir) - contextValues = project.velocity.contextValues - } - - project.compileJava.dependsOn(project.velocityVpp) - project.sourceSets.main.java.srcDir project.velocity.outputDir - - } -} - diff --git a/buildSrc/src/main/groovy/VelocityTask.groovy b/buildSrc/src/main/groovy/VelocityTask.groovy deleted file mode 100644 index 6a36903..0000000 --- a/buildSrc/src/main/groovy/VelocityTask.groovy +++ /dev/null @@ -1,58 +0,0 @@ -import org.apache.velocity.VelocityContext -import org.apache.velocity.app.VelocityEngine -import org.apache.velocity.runtime.log.SystemLogChute -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction - -class VelocityTask extends DefaultTask { - - @InputDirectory - File inputDir - - @OutputDirectory - File outputDir - - String filter = '**/*.java' - - File includeDir - - Map contextValues = [:] - - @TaskAction - void run() { - outputDir.deleteDir() - outputDir.mkdirs() - // println "Velocity: $inputDir -> $outputDir" - - VelocityEngine engine = new VelocityEngine() - engine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, SystemLogChute.class.name) - engine.setProperty(VelocityEngine.RESOURCE_LOADER, "file") - engine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_CACHE, "true") - if (includeDir != null) - engine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, includeDir.getAbsolutePath()) - def inputFiles = project.fileTree( - dir: inputDir, - include: filter - ) - inputFiles.visit { e -> - if (e.file.isFile()) { - File outputFile = e.relativePath.getFile(outputDir) - VelocityContext context = new VelocityContext() - contextValues.each { context.put(it.key, it.value) } - context.put('project', project) - context.put('package', e.relativePath.parent.segments.join('.')) - context.put('class', e.relativePath.lastName.replaceFirst("\\.java\$", "")) - // println "Parsing ${e.file}" - e.file.withReader { reader -> - outputFile.parentFile.mkdirs() - outputFile.withWriter { writer -> - engine.evaluate(context, writer, e.relativePath.toString(), reader) - } - } - } - } - } -} - diff --git a/codequality/HEADER b/codequality/HEADER deleted file mode 100644 index 3102e4b..0000000 --- a/codequality/HEADER +++ /dev/null @@ -1,13 +0,0 @@ -Copyright ${year} Netflix, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/codequality/checkstyle.xml b/codequality/checkstyle.xml deleted file mode 100644 index 47c01a2..0000000 --- a/codequality/checkstyle.xml +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 156e86d..0000000 --- a/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -version=1.4.1 diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle deleted file mode 100644 index 3163aa9..0000000 --- a/gradle/buildscript.gradle +++ /dev/null @@ -1,11 +0,0 @@ -// Executed in context of buildscript -repositories { - // Repo in addition to maven central - repositories { maven { url 'http://dl.bintray.com/content/netflixoss/external-gradle-plugins/' } } // For gradle-release -} -dependencies { - classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.6.1' - classpath 'com.mapvine:gradle-cobertura-plugin:0.1' - // classpath 'gradle-release:gradle-release:1.1.5' - classpath 'org.ajoberstar:gradle-git:0.5.0' -} diff --git a/gradle/check.gradle b/gradle/check.gradle deleted file mode 100644 index 78f8180..0000000 --- a/gradle/check.gradle +++ /dev/null @@ -1,25 +0,0 @@ -// Checkstyle -// apply plugin: 'checkstyle' -// checkstyle { -// ignoreFailures = true -// configFile = rootProject.file('codequality/checkstyle.xml') -// } - -// FindBugs -apply plugin: 'findbugs' -findbugs { - ignoreFailures = true -} -findbugsTest.enabled = false - -// PMD -// apply plugin: 'pmd' -// tasks.withType(Pmd) { reports.html.enabled true } - -apply plugin: 'cobertura' -cobertura { - sourceDirs = sourceSets.main.java.srcDirs - format = 'html' - includes = ['**/*.java', '**/*.groovy'] - excludes = [] -} diff --git a/gradle/convention.gradle b/gradle/convention.gradle deleted file mode 100644 index 22023ea..0000000 --- a/gradle/convention.gradle +++ /dev/null @@ -1,88 +0,0 @@ -apply plugin: 'java' // Plugin as major conventions, overwrites status - -sourceCompatibility = 1.5 - -// GRADLE-2087 workaround, perform after java plugin -status = project.hasProperty('preferredStatus')?project.preferredStatus:(version.contains('SNAPSHOT')?'snapshot':'release') - -// Indenting to align with multi-project branch - task sourcesJar(type: Jar, dependsOn:classes) { - from sourceSets.main.allSource - classifier 'sources' - extension 'jar' - } - - task javadocJar(type: Jar, dependsOn:javadoc) { - from javadoc.destinationDir - classifier 'javadoc' - extension 'jar' - } - - configurations.add('sources') - configurations.add('javadoc') - configurations.archives { - extendsFrom configurations.sources - extendsFrom configurations.javadoc - } - - // When outputing to an Ivy repo, we want to use the proper type field - gradle.taskGraph.whenReady { - def isNotMaven = !it.hasTask(project.uploadMavenCentral) - if (isNotMaven) { - def artifacts = project.configurations.sources.artifacts - def sourceArtifact = artifacts.iterator().next() - sourceArtifact.type = 'sources' - } - } - - artifacts { - sources(sourcesJar) { - // Weird Gradle quirk where type will be used for the extension, but only for sources - type 'jar' - } - javadoc(javadocJar) { - type 'javadoc' - } - } - - configurations { - provided { - description = 'much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive.' - transitive = true - visible = true - } - } - - project.sourceSets { - main.compileClasspath += project.configurations.provided - main.runtimeClasspath -= project.configurations.provided - test.compileClasspath += project.configurations.provided - test.runtimeClasspath += project.configurations.provided - } - -/* -apply plugin: 'github-pages' // Used to create publishGhPages task - -def docTasks = [:] -[Javadoc,ScalaDoc,Groovydoc].each{ Class docClass -> - tasks.withType(docClass).each { docTask -> - docTasks[docTask.name] = docTask - processGhPages.dependsOn(docTask) - } -} - -githubPages { - repoUri = "git@github.com:Netflix/${rootProject.githubProjectName}.git" - pages { - docTasks.each { shortName, docTask -> - from(docTask.outputs.files) { - into "docs/${shortName}" - } - } - } -} -*/ - -wrapper { - gradleVersion = '1.10' -} diff --git a/gradle/license.gradle b/gradle/license.gradle deleted file mode 100644 index 59b75b3..0000000 --- a/gradle/license.gradle +++ /dev/null @@ -1,8 +0,0 @@ -// Dependency for plugin was set in buildscript.gradle - -apply plugin: 'license' //nl.javadude.gradle.plugins.license.LicensePlugin -license { - header rootProject.file('codequality/HEADER') - ext.year = Calendar.getInstance().get(Calendar.YEAR) - skipExistingHeaders true -} diff --git a/gradle/maven.gradle b/gradle/maven.gradle deleted file mode 100644 index 0513719..0000000 --- a/gradle/maven.gradle +++ /dev/null @@ -1,69 +0,0 @@ -// Maven side of things -apply plugin: 'maven' // Java plugin has to have been already applied for the conf2scope mappings to work -apply plugin: 'signing' - -signing { - required { gradle.taskGraph.hasTask(uploadMavenCentral) } - sign configurations.archives -} - -/** - * Publishing to Maven Central example provided from http://jedicoder.blogspot.com/2011/11/automated-gradle-project-deployment-to.html - * artifactory will execute uploadArchives to force generation of ivy.xml, and we don't want that to trigger an upload to maven - * central, so using custom upload task. - */ -task uploadMavenCentral(type:Upload, dependsOn: signArchives) { - configuration = configurations.archives - onlyIf { ['release', 'snapshot'].contains(project.status) } - repositories.mavenDeployer { - beforeDeployment { signing.signPom(it) } - - // To test deployment locally, use the following instead of oss.sonatype.org - //repository(url: "file://localhost/${rootProject.rootDir}/repo") - - def sonatypeUsername = rootProject.hasProperty('sonatypeUsername')?rootProject.sonatypeUsername:'' - def sonatypePassword = rootProject.hasProperty('sonatypePassword')?rootProject.sonatypePassword:'' - - repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - - snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - - // Prevent datastamp from being appending to artifacts during deployment - uniqueVersion = false - - // Closure to configure all the POM with extra info, common to all projects - pom.project { - name "${project.name}" - description "Java C Preprocessor" - developers { - developer { - id 'shevek' - name 'Shevek' - email 'github@anarres.org' - } - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution 'repo' - } - } - url "https://github.com/shevek/${rootProject.githubProjectName}" - scm { - connection "scm:git:git@github.com:shevek/${rootProject.githubProjectName}.git" - url "scm:git:git@github.com:shevek/${rootProject.githubProjectName}.git" - developerConnection "scm:git:git@github.com:shevek/${rootProject.githubProjectName}.git" - } - issueManagement { - system 'github' - url "https://github.com/shevek/${rootProject.githubProjectName}/issues" - } - } - } -} - diff --git a/gradle/netflix-oss.gradle b/gradle/netflix-oss.gradle deleted file mode 100644 index a87bc54..0000000 --- a/gradle/netflix-oss.gradle +++ /dev/null @@ -1 +0,0 @@ -apply from: 'http://artifacts.netflix.com/gradle-netflix-local/artifactory.gradle' diff --git a/gradle/release.gradle b/gradle/release.gradle deleted file mode 100644 index 6e3c20e..0000000 --- a/gradle/release.gradle +++ /dev/null @@ -1,60 +0,0 @@ -apply plugin: 'release' - -[ uploadIvyLocal: 'uploadLocal', uploadArtifactory: 'artifactoryPublish', buildWithArtifactory: 'build' ].each { key, value -> - // Call out to compile against internal repository - task "${key}"(type: GradleBuild) { - startParameter = project.gradle.startParameter.newInstance() - doFirst { - startParameter.projectProperties = [status: project.status, preferredStatus: project.status] - } - startParameter.addInitScript( file('gradle/netflix-oss.gradle') ) - startParameter.getExcludedTaskNames().add('check') - tasks = [ 'build', value ] - } -} - -// Marker task for following code to key in on -task releaseCandidate(dependsOn: release) -task forceCandidate { - onlyIf { gradle.taskGraph.hasTask(releaseCandidate) } - doFirst { project.status = 'candidate' } -} -task forceRelease { - onlyIf { !gradle.taskGraph.hasTask(releaseCandidate) } - doFirst { project.status = 'release' } -} -release.dependsOn([forceCandidate, forceRelease]) - -task releaseSnapshot(dependsOn: [uploadArtifactory, uploadMavenCentral]) - -// Ensure our versions look like the project status before publishing -task verifyStatus << { - def hasSnapshot = version.contains('-SNAPSHOT') - if (project.status == 'snapshot' && !hasSnapshot) { - throw new GradleException("Version (${version}) needs -SNAPSHOT if publishing snapshot") - } -} -uploadArtifactory.dependsOn(verifyStatus) -uploadMavenCentral.dependsOn(verifyStatus) - -// Ensure upload happens before taggging, hence upload failures will leave repo in a revertable state -preTagCommit.dependsOn([uploadArtifactory, uploadMavenCentral]) - - -gradle.taskGraph.whenReady { taskGraph -> - def hasRelease = taskGraph.hasTask('commitNewVersion') - def indexOf = { return taskGraph.allTasks.indexOf(it) } - - if (hasRelease) { - assert indexOf(build) < indexOf(unSnapshotVersion), 'build target has to be after unSnapshotVersion' - assert indexOf(uploadMavenCentral) < indexOf(preTagCommit), 'preTagCommit has to be after uploadMavenCentral' - assert indexOf(uploadArtifactory) < indexOf(preTagCommit), 'preTagCommit has to be after uploadArtifactory' - } -} - -// Prevent plugin from asking for a version number interactively -ext.'gradle.release.useAutomaticVersion' = "true" - -release { - git.requireBranch = null -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 5838598..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 40aa5f8..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Dec 27 05:48:20 PST 2013 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip diff --git a/gradlew b/gradlew deleted file mode 100755 index 91a7e26..0000000 --- a/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index aec9973..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/lib/ant/ant-contrib.jar b/lib/ant/ant-contrib.jar deleted file mode 100644 index 6e04e84..0000000 Binary files a/lib/ant/ant-contrib.jar and /dev/null differ diff --git a/lib/checkstyle/checkstyle-all-4.1.jar b/lib/checkstyle/checkstyle-all-4.1.jar deleted file mode 100644 index ea018b6..0000000 Binary files a/lib/checkstyle/checkstyle-all-4.1.jar and /dev/null differ diff --git a/lib/checkstyle/commons-beanutils-core.jar b/lib/checkstyle/commons-beanutils-core.jar deleted file mode 100644 index ce79cbe..0000000 Binary files a/lib/checkstyle/commons-beanutils-core.jar and /dev/null differ diff --git a/lib/checkstyle/commons-cli.jar b/lib/checkstyle/commons-cli.jar deleted file mode 100644 index 22a004e..0000000 Binary files a/lib/checkstyle/commons-cli.jar and /dev/null differ diff --git a/lib/checkstyle/commons-collections.jar b/lib/checkstyle/commons-collections.jar deleted file mode 100644 index f66c6d2..0000000 Binary files a/lib/checkstyle/commons-collections.jar and /dev/null differ diff --git a/lib/checkstyle/commons-logging.jar b/lib/checkstyle/commons-logging.jar deleted file mode 100644 index b99c937..0000000 Binary files a/lib/checkstyle/commons-logging.jar and /dev/null differ diff --git a/lib/cobertura/asm-3.2.jar b/lib/cobertura/asm-3.2.jar deleted file mode 100644 index 2a83445..0000000 Binary files a/lib/cobertura/asm-3.2.jar and /dev/null differ diff --git a/lib/cobertura/asm-tree-3.2.jar b/lib/cobertura/asm-tree-3.2.jar deleted file mode 100644 index 4ab3528..0000000 Binary files a/lib/cobertura/asm-tree-3.2.jar and /dev/null differ diff --git a/lib/cobertura/cobertura.jar b/lib/cobertura/cobertura.jar deleted file mode 100644 index 438fe55..0000000 Binary files a/lib/cobertura/cobertura.jar and /dev/null differ diff --git a/lib/cobertura/jakarta-oro-2.0.8.jar b/lib/cobertura/jakarta-oro-2.0.8.jar deleted file mode 100644 index 23488d2..0000000 Binary files a/lib/cobertura/jakarta-oro-2.0.8.jar and /dev/null differ diff --git a/lib/findbugs/lib/annotations.jar b/lib/findbugs/lib/annotations.jar deleted file mode 100644 index e16db1f..0000000 Binary files a/lib/findbugs/lib/annotations.jar and /dev/null differ diff --git a/lib/findbugs/lib/asm-3.0.jar b/lib/findbugs/lib/asm-3.0.jar deleted file mode 100644 index 7a5c727..0000000 Binary files a/lib/findbugs/lib/asm-3.0.jar and /dev/null differ diff --git a/lib/findbugs/lib/asm-analysis-3.0.jar b/lib/findbugs/lib/asm-analysis-3.0.jar deleted file mode 100644 index c5f795f..0000000 Binary files a/lib/findbugs/lib/asm-analysis-3.0.jar and /dev/null differ diff --git a/lib/findbugs/lib/asm-commons-3.0.jar b/lib/findbugs/lib/asm-commons-3.0.jar deleted file mode 100644 index 2971530..0000000 Binary files a/lib/findbugs/lib/asm-commons-3.0.jar and /dev/null differ diff --git a/lib/findbugs/lib/asm-tree-3.0.jar b/lib/findbugs/lib/asm-tree-3.0.jar deleted file mode 100644 index c6347fe..0000000 Binary files a/lib/findbugs/lib/asm-tree-3.0.jar and /dev/null differ diff --git a/lib/findbugs/lib/asm-util-3.0.jar b/lib/findbugs/lib/asm-util-3.0.jar deleted file mode 100644 index 9515986..0000000 Binary files a/lib/findbugs/lib/asm-util-3.0.jar and /dev/null differ diff --git a/lib/findbugs/lib/asm-xml-3.0.jar b/lib/findbugs/lib/asm-xml-3.0.jar deleted file mode 100644 index b3d1a64..0000000 Binary files a/lib/findbugs/lib/asm-xml-3.0.jar and /dev/null differ diff --git a/lib/findbugs/lib/bcel.jar b/lib/findbugs/lib/bcel.jar deleted file mode 100644 index be093c8..0000000 Binary files a/lib/findbugs/lib/bcel.jar and /dev/null differ diff --git a/lib/findbugs/lib/buggy.icns b/lib/findbugs/lib/buggy.icns deleted file mode 100644 index b1f4660..0000000 Binary files a/lib/findbugs/lib/buggy.icns and /dev/null differ diff --git a/lib/findbugs/lib/dom4j-full.jar b/lib/findbugs/lib/dom4j-full.jar deleted file mode 100644 index 1efbf7e..0000000 Binary files a/lib/findbugs/lib/dom4j-full.jar and /dev/null differ diff --git a/lib/findbugs/lib/findbugs-ant.jar b/lib/findbugs/lib/findbugs-ant.jar deleted file mode 100644 index 3a2740e..0000000 Binary files a/lib/findbugs/lib/findbugs-ant.jar and /dev/null differ diff --git a/lib/findbugs/lib/findbugs.jar b/lib/findbugs/lib/findbugs.jar deleted file mode 100644 index 10c8f78..0000000 Binary files a/lib/findbugs/lib/findbugs.jar and /dev/null differ diff --git a/lib/findbugs/lib/findbugsGUI.jar b/lib/findbugs/lib/findbugsGUI.jar deleted file mode 100644 index 8cf6984..0000000 Binary files a/lib/findbugs/lib/findbugsGUI.jar and /dev/null differ diff --git a/lib/findbugs/lib/jsr305.jar b/lib/findbugs/lib/jsr305.jar deleted file mode 100644 index 90119cb..0000000 Binary files a/lib/findbugs/lib/jsr305.jar and /dev/null differ diff --git a/lib/findbugs/plugin/coreplugin.jar b/lib/findbugs/plugin/coreplugin.jar deleted file mode 100644 index 0f9c317..0000000 Binary files a/lib/findbugs/plugin/coreplugin.jar and /dev/null differ diff --git a/lib/findbugs/xsl/default.xsl b/lib/findbugs/xsl/default.xsl deleted file mode 100644 index e8f30d4..0000000 --- a/lib/findbugs/xsl/default.xsl +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - - - - - -&nbsp; - - - - - - Code - Warning - - - - - - - FindBugs Report - - - - - - - - - -

FindBugs Report

- -

Project Information

- - -

Metrics

- - -

Contents

- - -

Summary

- - - - - - - - - - - - tablerow0 - tablerow1 - - - - - - - - - - - tablerow0 - tablerow1 - - - - - - -
Warning TypeNumber
Warnings
Total
- -

Warnings

- -

Click on a warning row to see full context information.

- - - - - - - - - Warnings - Warnings_ - - - -

Details

- - - - - - - - -
- - -

Project: - - - - -

-

FindBugs version:

- -

Code analyzed:

-
    - -
  • -
    -
-



-
- - - - - - - - priority- - - - - - - - - - - - - - - - - - - - - -

:

- -
- - - - - - -

- - - - - - -
-
- - - - - -

lines of code analyzed, - in classes, - in packages.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MetricTotalDensity*
High Priority Warnings - - - - - - - - -
Medium Priority Warnings - - - - - - - - -
Low Priority Warnings - - - - - - - - -
Total Warnings
-

(* Defects per Thousand lines of non-commenting source statements)

-



- -
- -
- - diff --git a/lib/findbugs/xsl/fancy-hist.xsl b/lib/findbugs/xsl/fancy-hist.xsl deleted file mode 100644 index 8086600..0000000 --- a/lib/findbugs/xsl/fancy-hist.xsl +++ /dev/null @@ -1,1127 +0,0 @@ - - - - - - - - - - - - - - - - - - FindBugs (<xsl:value-of select="/BugCollection/@version" />) - Analysis for - <xsl:choose> - <xsl:when test='string-length(/BugCollection/Project/@projectName)>0'><xsl:value-of select="/BugCollection/Project/@projectName" /></xsl:when> - <xsl:otherwise><xsl:value-of select="/BugCollection/Project/@filename" /></xsl:otherwise> - </xsl:choose> - - - - - - -

- FindBugs () - Analysis for - - - - -

- - - -
- -
-
- Computing data... -
- -
-
-

Package Summary

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackageCode SizeBugsBugs p1Bugs p2Bugs p3Bugs Exp.
- Overall - ( packages), - ( classes) -
-
- -
-
-

Analyzed Files:

-
    - -
  • -
    -
-
-
-

Used Libraries:

-
    - -
  • -
    - -
  • None
  • -
    -
-
-
-

Analysis Errors:

-
    - - -
  • None
  • -
    - -
  • Missing ref classes for analysis: -
      - -
    • -
      -
    -
  • -
    -
-
-
-
Loading...
-
Loading...
-
Loading...
-
- - - - -
- - -
- diff --git a/lib/findbugs/xsl/fancy.xsl b/lib/findbugs/xsl/fancy.xsl deleted file mode 100644 index 5d152dd..0000000 --- a/lib/findbugs/xsl/fancy.xsl +++ /dev/null @@ -1,846 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - FindBugs (<xsl:value-of select="/BugCollection/@version" />) - Analysis for - <xsl:choose> - <xsl:when test='string-length(/BugCollection/Project/@projectName)>0'><xsl:value-of select="/BugCollection/Project/@projectName" /></xsl:when> - <xsl:otherwise><xsl:value-of select="/BugCollection/Project/@filename" /></xsl:otherwise> - </xsl:choose> - - - - - - -
-

- FindBugs () - Analysis for - - - - -

- - - - - - - - - - -
- tip- - tip - /
- -
-
- - - -
- b-uid-- - - - - - - -
-
-
-
-
- -
-
-
-
-
-
-
- - -
- - - - -
-

FindBugs Analysis generated at:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackageCode SizeBugsBugs p1Bugs p2Bugs p3Bugs Exp.Ratio
- Overall - ( packages), - ( classes) -
-
-
- - - - - - - - - - - - b-1 -    - P1 - - b-2 -    - P2 - - b-3 -    - P3 - - b-4 -    - Exp. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- category--and-code--and-bug- - category--and-code--and-bug- - b-uid-- - - - - - - - -
-
-
- - - - - - - - - - - 0 - - - - - - 0 - - - - - - 0 - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - - - - 0 - - - - - - 0 - - - - - - 0 - - - - - -
- -
- package--and-class- - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - -
- -
- package--and-class--and-type- - package--and-class--and-type- - b-uid-- - - - - - - - -
-
-
- -
diff --git a/lib/findbugs/xsl/plain.xsl b/lib/findbugs/xsl/plain.xsl deleted file mode 100644 index 80fff8d..0000000 --- a/lib/findbugs/xsl/plain.xsl +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - - Warning - Priority - Details - - - - - - - FindBugs Report - - - - - - - -

FindBugs Report

-

Produced using FindBugs .

-

Project: - - - - -

-

Metrics

- - -

Summary

- - - - - - - - - - - - tablerow0 - tablerow1 - - - - - - - - - - - tablerow0 - tablerow1 - - - - - - -
Warning TypeNumber
Warnings
Total
-



- -

Warnings

- -

Click on each warning link to see a full description of the issue, and - details of how to resolve it.

- - - - - - - - - Warnings - Warnings_ - - - -



-

Warning Types

- - - - - - - - -
- - - - - - - - - - - High - Medium - Low - Unknown - - - -



- - - -
In file , - - - line - - - lines - to - - -
- - -
-
-

- - -
- - -

- -



-
- - - - - - -

- - - - - - - - - - - - - - -

None

-



-
- - - - - -

lines of code analyzed, - in classes, - in packages.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MetricTotalDensity*
High Priority Warnings - - - - - - - - -
Medium Priority Warnings - - - - - - - - -
Low Priority Warnings - - - - - - - - -
Total Warnings - - - - - - - - - - -
-

(* Defects per Thousand lines of non-commenting source statements)

-



- -
- -
diff --git a/lib/findbugs/xsl/summary.xsl b/lib/findbugs/xsl/summary.xsl deleted file mode 100644 index faf0131..0000000 --- a/lib/findbugs/xsl/summary.xsl +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <xsl:value-of select="$PAGE.TITLE" /> - -

-

Analysis for - - - - - -

-

-

- -
-

- -
() -

- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - -
       
-
-
-
- -
diff --git a/lib/jrat/bcel-326809.jar b/lib/jrat/bcel-326809.jar deleted file mode 100644 index 9c8de92..0000000 Binary files a/lib/jrat/bcel-326809.jar and /dev/null differ diff --git a/lib/jrat/jmxri.jar b/lib/jrat/jmxri.jar deleted file mode 100644 index 5e519fd..0000000 Binary files a/lib/jrat/jmxri.jar and /dev/null differ diff --git a/lib/jrat/shiftone-arbor.jar b/lib/jrat/shiftone-arbor.jar deleted file mode 100644 index f16528e..0000000 Binary files a/lib/jrat/shiftone-arbor.jar and /dev/null differ diff --git a/lib/jrat/shiftone-jrat.jar b/lib/jrat/shiftone-jrat.jar deleted file mode 100644 index 3c1c5bf..0000000 Binary files a/lib/jrat/shiftone-jrat.jar and /dev/null differ diff --git a/lib/junit/junit.jar b/lib/junit/junit.jar deleted file mode 100644 index 717cd08..0000000 Binary files a/lib/junit/junit.jar and /dev/null differ diff --git a/lib/log4j/log4j-1.2.13.jar b/lib/log4j/log4j-1.2.13.jar deleted file mode 100644 index dde9972..0000000 Binary files a/lib/log4j/log4j-1.2.13.jar and /dev/null differ diff --git a/lib/runtime/getopt/gnu.getopt.jar b/lib/runtime/getopt/gnu.getopt.jar deleted file mode 100644 index 1aea24b..0000000 Binary files a/lib/runtime/getopt/gnu.getopt.jar and /dev/null differ diff --git a/lib/svn/commons-lang-2.0.jar b/lib/svn/commons-lang-2.0.jar deleted file mode 100644 index c8a2870..0000000 Binary files a/lib/svn/commons-lang-2.0.jar and /dev/null differ diff --git a/lib/svn/jakarta-regexp-1.3.jar b/lib/svn/jakarta-regexp-1.3.jar deleted file mode 100644 index d653a38..0000000 Binary files a/lib/svn/jakarta-regexp-1.3.jar and /dev/null differ diff --git a/lib/svn/svnClientAdapter.jar b/lib/svn/svnClientAdapter.jar deleted file mode 100644 index 502a53f..0000000 Binary files a/lib/svn/svnClientAdapter.jar and /dev/null differ diff --git a/lib/svn/svnant.jar b/lib/svn/svnant.jar deleted file mode 100644 index fc5e2b9..0000000 Binary files a/lib/svn/svnant.jar and /dev/null differ diff --git a/lib/svn/svnjavahl.jar b/lib/svn/svnjavahl.jar deleted file mode 100644 index 4208165..0000000 Binary files a/lib/svn/svnjavahl.jar and /dev/null differ diff --git a/lib/vpp/commons-collections-3.1.jar b/lib/vpp/commons-collections-3.1.jar deleted file mode 100644 index 41e230f..0000000 Binary files a/lib/vpp/commons-collections-3.1.jar and /dev/null differ diff --git a/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar b/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar deleted file mode 100644 index 3956a43..0000000 Binary files a/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar and /dev/null differ diff --git a/lib/vpp/velocity-1.4.jar b/lib/vpp/velocity-1.4.jar deleted file mode 100644 index 04ec9d2..0000000 Binary files a/lib/vpp/velocity-1.4.jar and /dev/null differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bb6ef10 --- /dev/null +++ b/pom.xml @@ -0,0 +1,124 @@ + + + 4.0.0 + org.anarres + cpp + Anarres JCPP + http://www.anarres.org/projects/jcpp/ + 1.5.1-SNAPSHOT + jar + + + + + anarres.author + Anarres Author + contact@anarres.org + + + + + + Apache 2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + UTF-8 + true + + + + + com.nativelibs4java + maven-velocity-plugin + 0.8 + + + generate-sources + + generate + + + + + + ${project.version} + ${project.version} + ${project.artifactId} + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.16 + + true + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + + package + create-my-bundle + + single + + + + + + + + + + junit + junit + 4.11 + test + + + org.apache.ant + ant + 1.8.2 + + + gnu.getopt + java-getopt + 1.0.13 + + + net.sourceforge.findbugs + jsr305 + 1.3.2 + + + + diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index f22856c..0000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name='jcpp' diff --git a/src/main/java/org/anarres/cpp/CppTask.java b/src/main/java/org/anarres/cpp/CppTask.java index 5e17786..55a8eff 100644 --- a/src/main/java/org/anarres/cpp/CppTask.java +++ b/src/main/java/org/anarres/cpp/CppTask.java @@ -34,7 +34,7 @@ */ public class CppTask extends Copy { - private class Listener extends PreprocessorListener { + private class Listener extends DefaultPreprocessorListener { @Override protected void print(String msg) { diff --git a/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java b/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java new file mode 100644 index 0000000..e6f122b --- /dev/null +++ b/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java @@ -0,0 +1,105 @@ +package org.anarres.cpp; + +/* + * Anarres C Preprocessor + * Copyright (c) 2007-2008, Shevek + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import javax.annotation.Nonnegative; + +/** + * A handler for preprocessor events, primarily errors and warnings. + * + * If no PreprocessorListener is installed in a Preprocessor, all + * error and warning events will throw an exception. Installing a + * listener allows more intelligent handling of these events. + */ +public class DefaultPreprocessorListener implements PreprocessorListener { + + private int errors; + private int warnings; + + public DefaultPreprocessorListener() { + clear(); + } + + public void clear() { + errors = 0; + warnings = 0; + } + + @Nonnegative + public int getErrors() { + return errors; + } + + @Nonnegative + public int getWarnings() { + return warnings; + } + + protected void print(String msg) { + System.err.println(msg); + } + + /** + * Handles a warning. + * + * The behaviour of this method is defined by the + * implementation. It may simply record the error message, or + * it may throw an exception. + */ + public void handleWarning(Source source, int line, int column, + String msg) + throws LexerException { + warnings++; + print(source.getName() + ":" + line + ":" + column + + ": warning: " + msg); + } + + /** + * Handles an error. + * + * The behaviour of this method is defined by the + * implementation. It may simply record the error message, or + * it may throw an exception. + */ + public void handleError(Source source, int line, int column, + String msg) + throws LexerException { + errors++; + print(source.getName() + ":" + line + ":" + column + + ": error: " + msg); + } + + public void handleSourceChange(Source source, String event) { + } + + @Override + public boolean beforeInclude(String filePath, int line) { + return false; + } + + public void handlePreprocesorDirective(Source source, PreprocessorDirective directive) { + System.err.println(directive.toString()); + } + + public void handleMacroExpansion(Source source, int line, int column, String macro, + String definitionFileName, int definitionLine, int definitionColumn) { + System.err.println(source.getName() + " : " + line + ":" + column + " macro: " + macro + + " definition: " + definitionFileName + ":" + definitionLine + ":" + + definitionColumn); + } + +} diff --git a/src/main/java/org/anarres/cpp/FileLexerSource.java b/src/main/java/org/anarres/cpp/FileLexerSource.java index 1505506..3f5a0d3 100644 --- a/src/main/java/org/anarres/cpp/FileLexerSource.java +++ b/src/main/java/org/anarres/cpp/FileLexerSource.java @@ -74,12 +74,12 @@ public File getFile() { * This is not necessarily the same as getFile().getPath() in case we are in a chroot. */ @Override - /* pp */ String getPath() { + public String getPath() { return path; } @Override - /* pp */ String getName() { + public String getName() { return getPath(); } diff --git a/src/main/java/org/anarres/cpp/InputLexerSource.java b/src/main/java/org/anarres/cpp/InputLexerSource.java index 3e1dcb3..5eb8f94 100644 --- a/src/main/java/org/anarres/cpp/InputLexerSource.java +++ b/src/main/java/org/anarres/cpp/InputLexerSource.java @@ -48,12 +48,12 @@ public InputLexerSource(InputStream input) } @Override - /* pp */ String getPath() { + public String getPath() { return ""; } @Override - /* pp */ String getName() { + public String getName() { return "standard input"; } diff --git a/src/main/java/org/anarres/cpp/LexerSource.java b/src/main/java/org/anarres/cpp/LexerSource.java index 5f1dac3..4f5362e 100644 --- a/src/main/java/org/anarres/cpp/LexerSource.java +++ b/src/main/java/org/anarres/cpp/LexerSource.java @@ -120,10 +120,10 @@ private void _error(String msg, boolean error) } /* - private boolean _isLineSeparator(int c) { - return Character.getType(c) == Character.LINE_SEPARATOR - || c == -1; - } + * private boolean _isLineSeparator(int c) { + * return Character.getType(c) == Character.LINE_SEPARATOR + * || c == -1; + * } */ /* XXX Move to JoinReader and canonicalise newlines. */ @@ -197,14 +197,14 @@ private int read() } /* - if (isLineSeparator(c)) { - line++; - lastcolumn = column; - column = 0; - } - else { - column++; - } + * if (isLineSeparator(c)) { + * line++; + * lastcolumn = column; + * column = 0; + * } + * else { + * column++; + * } */ return c; } @@ -241,7 +241,7 @@ private void unread(int c) /* Consumes the rest of the current line into an invalid. */ @Nonnull - private Token invalid(StringBuilder text, String reason) + private Token invalid(StringBuilder text, int expected, String reason) throws IOException, LexerException { int d = read(); @@ -250,25 +250,33 @@ private Token invalid(StringBuilder text, String reason) d = read(); } unread(d); - return new Token(INVALID, text.toString(), reason); + return new Token(INVALID, expected, text.toString(), reason); } @Nonnull private Token ccomment() throws IOException, LexerException { + StringBuilder text = new StringBuilder("/*"); int d; do { do { d = read(); - text.append((char) d); - } while (d != '*'); + if (d != -1) { + text.append((char) d); + } + } while (d != '*' && d != -1); do { d = read(); - text.append((char) d); + if (d != -1) { + text.append((char) d); + } } while (d == '*'); - } while (d != '/'); + } while (d != '/' && d != -1); + if (d == -1) { + return new Token(INVALID, CCOMMENT, text.toString(), "End of file in a multiline comment"); + } return new Token(CCOMMENT, text.toString()); } @@ -375,15 +383,15 @@ private Token character() d = escape(text); } else if (isLineSeparator(d)) { unread(d); - return new Token(INVALID, text.toString(), + return new Token(INVALID, CHARACTER, text.toString(), "Unterminated character literal"); } else if (d == '\'') { text.append('\''); - return new Token(INVALID, text.toString(), + return new Token(INVALID, CHARACTER, text.toString(), "Empty character literal"); } else if (!Character.isDefined(d)) { text.append('?'); - return invalid(text, "Illegal unicode character literal"); + return invalid(text, CHARACTER, "Illegal unicode character literal"); } else { text.append((char) d); } @@ -402,7 +410,7 @@ private Token character() break; e = read(); } - return new Token(INVALID, text.toString(), + return new Token(INVALID, CHARACTER, text.toString(), "Illegal character constant " + text); } text.append('\''); @@ -420,6 +428,26 @@ private Token string(char open, char close) StringBuilder buf = new StringBuilder(); + final int expectedTokenType; + final String expectedTokenName; + switch (open) { + case '\"': + expectedTokenType = Token.STRING; + expectedTokenName = "string"; + break; + case '\'': + expectedTokenType = Token.CHARACTER; + expectedTokenName = "character"; + break; + case '>': + expectedTokenType = Token.HEADER; + expectedTokenName = "header"; + break; + default: + expectedTokenType = 0; + expectedTokenName = "string"; + } + for (;;) { int c = read(); if (c == close) { @@ -433,13 +461,13 @@ private Token string(char open, char close) } else if (c == -1) { unread(c); // error("End of file in string literal after " + buf); - return new Token(INVALID, text.toString(), - "End of file in string literal after " + buf); + return new Token(INVALID, expectedTokenType, text.toString(), + "End of file in " + expectedTokenName + " literal after " + buf); } else if (isLineSeparator(c)) { unread(c); // error("Unterminated string literal after " + buf); - return new Token(INVALID, text.toString(), - "Unterminated string literal after " + buf); + return new Token(INVALID, expectedTokenType, text.toString(), + "Unterminated " + expectedTokenName + " literal after " + buf); } else { text.append((char) c); buf.append((char) c); @@ -479,7 +507,7 @@ private Token _number_suffix(StringBuilder text, NumericValue value, int d) d = read(); } else if (d == 'L' || d == 'l') { if ((flags & NumericValue.FF_SIZE) != 0) - warning("Nultiple length suffixes after " + text); + warning("Multiple length suffixes after " + text); text.append((char) d); int e = read(); if (e == d) { // Case must match. Ll is Welsh. @@ -492,19 +520,19 @@ private Token _number_suffix(StringBuilder text, NumericValue value, int d) } } else if (d == 'I' || d == 'i') { if ((flags & NumericValue.FF_SIZE) != 0) - warning("Nultiple length suffixes after " + text); + warning("Multiple length suffixes after " + text); flags |= NumericValue.F_INT; text.append((char) d); d = read(); } else if (d == 'F' || d == 'f') { if ((flags & NumericValue.FF_SIZE) != 0) - warning("Nultiple length suffixes after " + text); + warning("Multiple length suffixes after " + text); flags |= NumericValue.F_FLOAT; text.append((char) d); d = read(); } else if (d == 'D' || d == 'd') { if ((flags & NumericValue.FF_SIZE) != 0) - warning("Nultiple length suffixes after " + text); + warning("Multiple length suffixes after " + text); flags |= NumericValue.F_DOUBLE; text.append((char) d); d = read(); @@ -512,7 +540,7 @@ private Token _number_suffix(StringBuilder text, NumericValue value, int d) else if (Character.isLetter(d) || d == '_') { unread(d); value.setFlags(flags); - return invalid(text, + return invalid(text, Token.NUMBER, "Invalid suffix \"" + (char) d + "\" on numeric constant"); } else { @@ -526,11 +554,16 @@ else if (Character.isLetter(d) || d == '_') { /* Either a decimal part, or a hex exponent. */ @Nonnull - private String _number_part(StringBuilder text, int base) + private String _number_part(StringBuilder text, int base, boolean sign) throws IOException, LexerException { StringBuilder part = new StringBuilder(); int d = read(); + if (sign && d == '-') { + text.append((char) d); + part.append((char) d); + d = read(); + } while (Character.digit(d, base) != -1) { text.append((char) d); part.append((char) d); @@ -540,18 +573,6 @@ private String _number_part(StringBuilder text, int base) return part.toString(); } - /* We already chewed a zero, so empty is fine. */ - @Nonnull - private Token number_octal() - throws IOException, - LexerException { - StringBuilder text = new StringBuilder("0"); - String integer = _number_part(text, 8); - int d = read(); - NumericValue value = new NumericValue(8, integer); - return _number_suffix(text, value, d); - } - /* We do not know whether know the first digit is valid. */ @Nonnull private Token number_hex(char x) @@ -559,23 +580,34 @@ private Token number_hex(char x) LexerException { StringBuilder text = new StringBuilder("0"); text.append(x); - String integer = _number_part(text, 16); + String integer = _number_part(text, 16, false); NumericValue value = new NumericValue(16, integer); int d = read(); if (d == '.') { - String fraction = _number_part(text, 16); + text.append((char) d); + String fraction = _number_part(text, 16, false); value.setFractionalPart(fraction); d = read(); } if (d == 'P' || d == 'p') { - String exponent = _number_part(text, 10); - value.setExponent(exponent); + text.append((char) d); + String exponent = _number_part(text, 10, true); + value.setExponent(2, exponent); d = read(); } // XXX Make sure it's got enough parts return _number_suffix(text, value, d); } + private static boolean is_octal(@Nonnull String text) { + if (!text.startsWith("0")) + return false; + for (int i = 0; i < text.length(); i++) + if (Character.digit(text.charAt(i), 8) == -1) + return false; + return true; + } + /* We know we have at least one valid digit, but empty is not * fine. */ @Nonnull @@ -583,25 +615,95 @@ private Token number_decimal() throws IOException, LexerException { StringBuilder text = new StringBuilder(); - String integer = _number_part(text, 10); - NumericValue value = new NumericValue(10, integer); + String integer = _number_part(text, 10, false); + String fraction = null; + String exponent = null; int d = read(); if (d == '.') { text.append((char) d); - String fraction = _number_part(text, 10); - value.setFractionalPart(fraction); + fraction = _number_part(text, 10, false); d = read(); } if (d == 'E' || d == 'e') { text.append((char) d); - String exponent = _number_part(text, 10); - value.setExponent(exponent); + exponent = _number_part(text, 10, true); d = read(); } + int base = 10; + if (fraction == null && exponent == null && integer.startsWith("0")) { + if (!is_octal(integer)) + warning("Decimal constant starts with 0, but not octal: " + integer); + else + base = 8; + } + NumericValue value = new NumericValue(base, integer); + if (fraction != null) + value.setFractionalPart(fraction); + if (exponent != null) + value.setExponent(10, exponent); // XXX Make sure it's got enough parts return _number_suffix(text, value, d); } + /** + * Section 6.4.4.1 of C99 + * + * (Not pasted here, but says that the initial negation is a separate token.) + * + * Section 6.4.4.2 of C99 + * + * A floating constant has a significand part that may be followed + * by an exponent part and a suffix that specifies its type. The + * components of the significand part may include a digit sequence + * representing the whole-number part, followed by a period (.), + * followed by a digit sequence representing the fraction part. + * + * The components of the exponent part are an e, E, p, or P + * followed by an exponent consisting of an optionally signed digit + * sequence. Either the whole-number part or the fraction part has to + * be present; for decimal floating constants, either the period or + * the exponent part has to be present. + * + * The significand part is interpreted as a (decimal or hexadecimal) + * rational number; the digit sequence in the exponent part is + * interpreted as a decimal integer. For decimal floating constants, + * the exponent indicates the power of 10 by which the significand + * part is to be scaled. For hexadecimal floating constants, the + * exponent indicates the power of 2 by which the significand part is + * to be scaled. + * + * For decimal floating constants, and also for hexadecimal + * floating constants when FLT_RADIX is not a power of 2, the result + * is either the nearest representable value, or the larger or smaller + * representable value immediately adjacent to the nearest representable + * value, chosen in an implementation-defined manner. For hexadecimal + * floating constants when FLT_RADIX is a power of 2, the result is + * correctly rounded. + */ + @Nonnull + private Token number() + throws IOException, + LexerException { + Token tok; + int c = read(); + if (c == '0') { + int d = read(); + if (d == 'x' || d == 'X') { + tok = number_hex((char) d); + } else { + unread(d); + unread(c); + tok = number_decimal(); + } + } else if (Character.isDigit(c) || c == '.') { + unread(c); + tok = number_decimal(); + } else { + throw new LexerException("Asked to parse something as a number which isn't: " + (char) c); + } + return tok; + } + @Nonnull private Token identifier(int c) throws IOException, @@ -611,11 +713,13 @@ private Token identifier(int c) text.append((char) c); for (;;) { d = read(); - if (Character.isIdentifierIgnorable(d)) - ; else if (Character.isJavaIdentifierPart(d)) + if (Character.isIdentifierIgnorable(d)) { + ; + } else if (Character.isJavaIdentifierPart(d)) { text.append((char) d); - else + } else { break; + } } unread(d); return new Token(IDENTIFIER, text.toString()); @@ -839,26 +943,11 @@ else if (d == '=') unread(d); if (Character.isDigit(d)) { unread('.'); - tok = number_decimal(); + tok = number(); } /* XXX decimal fraction */ break; - case '0': - /* octal or hex */ - d = read(); - if (d == 'x' || d == 'X') - tok = number_hex((char) d); - else if (d == '.') { - unread(d); - unread(c); - tok = number_decimal(); - } else { - unread(d); - tok = number_octal(); - } - break; - case '\'': tok = string('\'', '\''); break; @@ -878,7 +967,7 @@ else if (d == '.') { tok = whitespace(c); } else if (Character.isDigit(c)) { unread(c); - tok = number_decimal(); + tok = number(); } else if (Character.isJavaIdentifierStart(c)) { tok = identifier(c); } else { @@ -904,6 +993,7 @@ else if (d == '.') { return tok; } + @Override public void close() throws IOException { if (reader != null) { diff --git a/src/main/java/org/anarres/cpp/Macro.java b/src/main/java/org/anarres/cpp/Macro.java index 534cb2b..c69b693 100644 --- a/src/main/java/org/anarres/cpp/Macro.java +++ b/src/main/java/org/anarres/cpp/Macro.java @@ -29,6 +29,11 @@ * extra tokens {@link Token#M_ARG} and {@link Token#M_STRING}. */ public class Macro { + /** + * A value for indicating an unknown position of a macro name. + */ + public static final int UNKNOWN_POSITION_VALUE = -1; + private Source source; private String name; /* It's an explicit decision to keep these around here. We don't @@ -39,12 +44,32 @@ public class Macro { private boolean variadic; private List tokens; - public Macro(Source source, String name) { + /** + * Number of the line that the name of the macro in its definition starts + * in. It is set to UNKNOWN_POSITION_VALUE if it is unknown, e.g. if the + * macro does not come from any file. + */ + private final int nameStartLine; + + /** + * Number of the column that the name of the macro in its definition + * starts in. It is set to UNKNOWN_POSITION_VALUE if it us unknown. + */ + private final int nameStartColumn; + + + public Macro(Source source, String name, int nameStartLine, int nameStartColumn) { this.source = source; this.name = name; this.args = null; this.variadic = false; this.tokens = new ArrayList(); + this.nameStartLine = nameStartLine; + this.nameStartColumn = nameStartColumn; + } + + public Macro(Source source, String name) { + this(source, name, UNKNOWN_POSITION_VALUE, UNKNOWN_POSITION_VALUE); } public Macro(String name) { @@ -96,6 +121,18 @@ public int getArgs() { return args.size(); } + /** + * @return The number of the line the name of the macro in its definition + * starts or UNKNOWN_POSITION_VALUE if it is unknown. + */ + public int getNameStartLine() { return nameStartLine; } + + /** + * @return The number of the column the name of the macro in its definition + * starts or UNKNOWN_POSITION_VALUE if it is unknown. + */ + public int getNameStartColumn() { return nameStartColumn; } + /** * Sets the variadic flag on this Macro. */ diff --git a/src/main/java/org/anarres/cpp/MacroTokenSource.java b/src/main/java/org/anarres/cpp/MacroTokenSource.java index 516e4f2..6fa4970 100644 --- a/src/main/java/org/anarres/cpp/MacroTokenSource.java +++ b/src/main/java/org/anarres/cpp/MacroTokenSource.java @@ -17,6 +17,7 @@ package org.anarres.cpp; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -32,12 +33,54 @@ private final List args; /* { unexpanded, expanded } */ private Iterator arg; /* "current expansion" */ + private boolean argumentPasted; /* do tokens from 'arg' come from a paste? */ - /* pp */ MacroTokenSource(Macro m, List args) { + /** + * Token with the macro identifier that tokens from this source replace. + */ + private final Token originalToken; + + /* pp */ MacroTokenSource(Macro m, List args, Token originalToken) { this.macro = m; - this.tokens = m.getTokens().iterator(); + + /* Clone all tokens from the macro definition. It is done to keep the + appropriate original tokens in those that are returned by + MacroTokenSource objects. Cloning tokens solves a problem with that + while nesting the same macro in itself, e.g. ADD(1, ADD(1, 2)) + where ADD is defined as follows: #define ADD(x, y) x + y */ + final List clonedTokens = new ArrayList<>(); + try { + for (Token token : m.getTokens()) { + clonedTokens.add((Token) token.clone()); + } + + } catch(CloneNotSupportedException e) { + /* This type of exception should never be thrown in the above code + because Token class implements 'clone' method and Cloneable + interface properly */ + throw new RuntimeException("MacroTokenSource.: CloneNotSupportedException" + + " caught\n" + e.getMessage()); + } + this.tokens = clonedTokens.iterator(); + this.args = args; this.arg = null; + this.originalToken = originalToken; + this.argumentPasted = false; + } + + @Override + MacroTokenSource getExpandingRoot() { + final Source parent = getParent(); + if (parent == null || !parent.isExpanding()) { + return this; + } + return parent.getExpandingRoot(); + } + + @Override + boolean isExpanding() { + return true; } @Override @@ -81,6 +124,31 @@ private void concat(StringBuilder buf, Argument arg) { } private Token stringify(Token pos, Argument arg) { + // Set the data for the position tracing + final MacroTokenSource expandingRoot = getExpandingRoot(); + assert expandingRoot != null; + final Token expandingRootToken = expandingRoot.getOriginalToken(); + Token origToken = null; // original token to set in the result of this method + if (arg.size() > 0) { + final Token argFirstTok = arg.get(0); + final Token argFirstTokOrigTok = argFirstTok.getOriginalMacroToken(); + + if (expandingRoot.argumentContains(argFirstTok)) { + pos = argFirstTokOrigTok != null + ? argFirstTokOrigTok + : argFirstTok; + origToken = argFirstTokOrigTok != null + ? argFirstTokOrigTok + : null; + } else { + pos = expandingRootToken; + origToken = expandingRootToken; + } + } else { + pos = expandingRootToken; + origToken = expandingRootToken; + } + StringBuilder buf = new StringBuilder(); concat(buf, arg); // System.out.println("Concat: " + arg + " -> " + buf); @@ -88,9 +156,15 @@ private Token stringify(Token pos, Argument arg) { escape(str, buf); str.append("\""); // System.out.println("Escape: " + buf + " -> " + str); - return new Token(STRING, + + final Token result = new Token(STRING, pos.getLine(), pos.getColumn(), str.toString(), buf.toString()); + result.setStringized(true); + if (origToken != null) { + result.setOriginalMacroToken(origToken); + } + return result; } @@ -146,6 +220,7 @@ private void paste(Token ptok) /* XXX Check that concatenation produces a valid token. */ arg = new SourceIterator(sl); + argumentPasted = true; } public Token token() @@ -160,9 +235,10 @@ public Token token() /* XXX PASTE -> INVALID. */ assert tok.getType() != M_PASTE : "Unexpected paste token"; - return tok; + return argumentPasted ? withOriginalToken(tok) : tok; } arg = null; + argumentPasted = false; } if (!tokens.hasNext()) @@ -185,12 +261,65 @@ public Token token() paste(tok); break; default: - return tok; + return withOriginalToken(tok); } } /* for */ } + /** + * Sets the original token of the given one to the original token from the + * source code (but not from a macro definition) that caused expansion of + * a macro that this object is result of. + * + * @param token Token whose original one is to be assigned. + * @return The given token. + */ + private Token withOriginalToken(Token token) { + assert token != null; + token.setOriginalMacroToken(getExpandingRootToken()); + return token; + } + + /** + * @return Token with a macro identifier that caused the appearance of this + * Source object. + */ + Token getOriginalToken() { + return originalToken; + } + + /** + * @return True if and only if the given token is a token from an argument + * expansion or the actual argument of a macro use that this object + * represents. + */ + boolean argumentContains(Token tok) { + if (args == null) { + return false; + } + + for (Argument arg : args) { + // Check if the token comes from an argument expansion + final Iterator argumentExpansion = arg.expansion(); + while (argumentExpansion.hasNext()) { + if (argumentExpansion.next() == tok) { + return true; + } + } + + /* Check if the token comes from the original expression passed as + an argument (before its expansion) */ + for (Token argTokBeforeExpansion : arg) { + if (argTokBeforeExpansion == tok) { + return true; + } + } + } + + return false; + } + @Override public String toString() { StringBuilder buf = new StringBuilder(); diff --git a/src/main/java/org/anarres/cpp/Main.java b/src/main/java/org/anarres/cpp/Main.java index 1902798..4c50b70 100644 --- a/src/main/java/org/anarres/cpp/Main.java +++ b/src/main/java/org/anarres/cpp/Main.java @@ -92,7 +92,7 @@ public void run(String[] args) throws Exception { pp.addFeature(Feature.TRIGRAPHS); pp.addFeature(Feature.LINEMARKERS); pp.addWarning(Warning.IMPORT); - pp.setListener(new PreprocessorListener()); + pp.setListener(new DefaultPreprocessorListener()); pp.addMacro("__JCPP__"); pp.getSystemIncludePath().add("/usr/local/include"); pp.getSystemIncludePath().add("/usr/include"); diff --git a/src/main/java/org/anarres/cpp/NumericValue.java b/src/main/java/org/anarres/cpp/NumericValue.java index 8d961c4..e38d28f 100644 --- a/src/main/java/org/anarres/cpp/NumericValue.java +++ b/src/main/java/org/anarres/cpp/NumericValue.java @@ -18,6 +18,10 @@ import java.math.BigDecimal; import java.math.BigInteger; +import javax.annotation.CheckForNull; +import javax.annotation.CheckForSigned; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; public class NumericValue extends Number { @@ -33,6 +37,7 @@ public class NumericValue extends Number { private final int base; private final String integer; private String fraction; + private int expbase = 0; private String exponent; private int flags; @@ -41,14 +46,17 @@ public NumericValue(int base, String integer) { this.integer = integer; } + @Nonnegative public int getBase() { return base; } + @Nonnull public String getIntegerPart() { return integer; } + @CheckForNull public String getFractionalPart() { return fraction; } @@ -57,11 +65,18 @@ public String getFractionalPart() { this.fraction = fraction; } + @CheckForSigned + public int getExponentBase() { + return expbase; + } + + @CheckForNull public String getExponent() { return exponent; } - /* pp */ void setExponent(String exponent) { + /* pp */ void setExponent(int expbase, String exponent) { + this.expbase = expbase; this.exponent = exponent; } @@ -78,6 +93,7 @@ public int getFlags() { * precision numbers is nontrivial, and this routine gets it wrong * in many important cases. */ + @Nonnull public BigDecimal toBigDecimal() { int scale = 0; String text = getIntegerPart(); @@ -93,6 +109,7 @@ public BigDecimal toBigDecimal() { return new BigDecimal(unscaled, scale); } + @Nonnull public Number toJavaLangNumber() { int flags = getFlags(); if ((flags & F_DOUBLE) != 0) @@ -111,23 +128,41 @@ else if (getExponent() != null) return intValue(); } + private int exponentValue() { + return Integer.parseInt(exponent, 10); + } + @Override public int intValue() { - return Integer.parseInt(toString()); + int v = integer.isEmpty() ? 0 : Integer.parseInt(integer, base); + if (expbase == 2) + v = v << exponentValue(); + else if (expbase != 0) + v = (int) (v * Math.pow(expbase, exponentValue())); + return v; } @Override public long longValue() { - return Long.parseLong(toString()); + long v = integer.isEmpty() ? 0 : Long.parseLong(integer, base); + if (expbase == 2) + v = v << exponentValue(); + else if (expbase != 0) + v = (int) (v * Math.pow(expbase, exponentValue())); + return v; } @Override public float floatValue() { + if (getBase() != 10) + return longValue(); return Float.parseFloat(toString()); } @Override public double doubleValue() { + if (getBase() != 10) + return longValue(); return Double.parseDouble(toString()); } diff --git a/src/main/java/org/anarres/cpp/Preprocessor.java b/src/main/java/org/anarres/cpp/Preprocessor.java index e4ecdc1..9ba241e 100644 --- a/src/main/java/org/anarres/cpp/Preprocessor.java +++ b/src/main/java/org/anarres/cpp/Preprocessor.java @@ -47,31 +47,31 @@ * {@link CppReader}, which does this.) */ /* - Source file name and line number information is conveyed by lines of the form - - # linenum filename flags - - These are called linemarkers. They are inserted as needed into - the output (but never within a string or character constant). They - mean that the following line originated in file filename at line - linenum. filename will never contain any non-printing characters; - they are replaced with octal escape sequences. - - After the file name comes zero or more flags, which are `1', `2', - `3', or `4'. If there are multiple flags, spaces separate them. Here - is what the flags mean: - - `1' - This indicates the start of a new file. - `2' - This indicates returning to a file (after having included another - file). - `3' - This indicates that the following text comes from a system header - file, so certain warnings should be suppressed. - `4' - This indicates that the following text should be treated as being - wrapped in an implicit extern "C" block. + * Source file name and line number information is conveyed by lines of the form + * + * # linenum filename flags + * + * These are called linemarkers. They are inserted as needed into + * the output (but never within a string or character constant). They + * mean that the following line originated in file filename at line + * linenum. filename will never contain any non-printing characters; + * they are replaced with octal escape sequences. + * + * After the file name comes zero or more flags, which are `1', `2', + * `3', or `4'. If there are multiple flags, spaces separate them. Here + * is what the flags mean: + * + * `1' + * This indicates the start of a new file. + * `2' + * This indicates returning to a file (after having included another + * file). + * `3' + * This indicates that the following text comes from a system header + * file, so certain warnings should be suppressed. + * `4' + * This indicates that the following text should be treated as being + * wrapped in an implicit extern "C" block. */ public class Preprocessor implements Closeable { @@ -292,7 +292,7 @@ public void addInput(@Nonnull File file) * If a PreprocessorListener is installed, it receives the * error. Otherwise, an exception is thrown. */ - protected void error(int line, int column, String msg) + protected void error(int line, int column, @Nonnull String msg) throws LexerException { if (listener != null) listener.handleError(source, line, column, msg); @@ -300,6 +300,43 @@ protected void error(int line, int column, String msg) throw new LexerException("Error at " + line + ":" + column + ": " + msg); } + /** + * Handles a preprocessor directive. + * + * If a PreprocessorListener is installed, it receives the + * directive. Otherwise, it is ignored. + */ + protected void directive(PreprocessorDirective directive) { + if (listener != null) { + listener.handlePreprocesorDirective(getSource(), directive); + } + } + + protected void macroExpansion(Macro macro, Token identifier, boolean inExpandingSources) { + if (listener != null && (inExpandingSources || !getSource().isExpanding())) { + // Source can be null + final String definitionFileName = macro.getSource() != null ? macro.getSource().getPath() : null; + + listener.handleMacroExpansion(getSource(), identifier.getLine(), + identifier.getColumn(), identifier.getText(), definitionFileName, + macro.getNameStartLine(), macro.getNameStartColumn()); + } + } + + /** + * Marks all tokens that are part of macro definition. Therefore, when + * the macro is expanded and macro's tokens are returned by preprocessor, + * these tokens can be easily distinguished from tokens that comes from + * source file. + * + * @param m macro + */ + protected void markTokens(Macro m) { + for (Token token : m.getTokens()) { + token.setExpanded(true); + } + } + /** * Handles an error. * @@ -308,7 +345,7 @@ protected void error(int line, int column, String msg) * * @see #error(int, int, String) */ - protected void error(Token tok, String msg) + protected void error(@Nonnull Token tok, @Nonnull String msg) throws LexerException { error(tok.getLine(), tok.getColumn(), msg); } @@ -319,7 +356,7 @@ protected void error(Token tok, String msg) * If a PreprocessorListener is installed, it receives the * warning. Otherwise, an exception is thrown. */ - protected void warning(int line, int column, String msg) + protected void warning(int line, int column, @Nonnull String msg) throws LexerException { if (warnings.contains(Warning.ERROR)) error(line, column, msg); @@ -337,7 +374,7 @@ else if (listener != null) * * @see #warning(int, int, String) */ - protected void warning(Token tok, String msg) + protected void warning(@Nonnull Token tok, @Nonnull String msg) throws LexerException { warning(tok.getLine(), tok.getColumn(), msg); } @@ -348,12 +385,12 @@ protected void warning(Token tok, String msg) * The given {@link Macro} object encapsulates both the name * and the expansion. */ - public void addMacro(Macro m) throws LexerException { - // System.out.println("Macro " + m); + public void addMacro(@Nonnull Macro m) throws LexerException { String name = m.getName(); /* Already handled as a source error in macro(). */ if ("defined".equals(name)) throw new LexerException("Cannot redefine name 'defined'"); + markTokens(m); macros.put(m.getName(), m); } @@ -363,7 +400,7 @@ public void addMacro(Macro m) throws LexerException { * The String value is lexed into a token stream, which is * used as the macro expansion. */ - public void addMacro(String name, String value) + public void addMacro(@Nonnull String name, @Nonnull String value) throws LexerException { try { Macro m = new Macro(name); @@ -386,7 +423,7 @@ public void addMacro(String name, String value) * This is a convnience method, and is equivalent to * addMacro(name, "1"). */ - public void addMacro(String name) + public void addMacro(@Nonnull String name) throws LexerException { addMacro(name, "1"); } @@ -395,7 +432,7 @@ public void addMacro(String name) * Sets the user include path used by this Preprocessor. */ /* Note for future: Create an IncludeHandler? */ - public void setQuoteIncludePath(List path) { + public void setQuoteIncludePath(@Nonnull List path) { this.quoteincludepath = path; } @@ -404,6 +441,7 @@ public void setQuoteIncludePath(List path) { * * This list may be freely modified by user code. */ + @Nonnull public List getQuoteIncludePath() { return quoteincludepath; } @@ -412,7 +450,7 @@ public List getQuoteIncludePath() { * Sets the system include path used by this Preprocessor. */ /* Note for future: Create an IncludeHandler? */ - public void setSystemIncludePath(List path) { + public void setSystemIncludePath(@Nonnull List path) { this.sysincludepath = path; } @@ -421,6 +459,7 @@ public void setSystemIncludePath(List path) { * * This list may be freely modified by user code. */ + @Nonnull public List getSystemIncludePath() { return sysincludepath; } @@ -429,7 +468,7 @@ public List getSystemIncludePath() { * Sets the Objective-C frameworks path used by this Preprocessor. */ /* Note for future: Create an IncludeHandler? */ - public void setFrameworksPath(List path) { + public void setFrameworksPath(@Nonnull List path) { this.frameworkspath = path; } @@ -439,6 +478,7 @@ public void setFrameworksPath(List path) { * * This list may be freely modified by user code. */ + @Nonnull public List getFrameworksPath() { return frameworkspath; } @@ -447,6 +487,7 @@ public List getFrameworksPath() { * Returns the Map of Macros parsed during the run of this * Preprocessor. */ + @Nonnull public Map getMacros() { return macros; } @@ -457,6 +498,7 @@ public Map getMacros() { * While you can modify the returned object, unexpected things * might happen if you do. */ + @CheckForNull public Macro getMacro(String name) { return macros.get(name); } @@ -765,8 +807,8 @@ private boolean macro(Macro m, Token orig) } /* - for (Argument a : args) - a.expand(this); + * for (Argument a : args) + * a.expand(this); */ for (int i = 0; i < args.size(); i++) { args.get(i).expand(this); @@ -783,12 +825,14 @@ private boolean macro(Macro m, Token orig) args = null; } + macroExpansion(m, orig, false); + if (m == __LINE__) { push_source(new FixedTokenSource( new Token[]{new Token(NUMBER, orig.getLine(), orig.getColumn(), - String.valueOf(orig.getLine()), - new NumericValue(10, "" + orig.getLine()))} + Integer.toString(orig.getLine()), + new NumericValue(10, Integer.toString(orig.getLine())))} ), true); } else if (m == __FILE__) { StringBuilder buf = new StringBuilder("\""); @@ -823,11 +867,11 @@ private boolean macro(Macro m, Token orig) push_source(new FixedTokenSource( new Token[]{new Token(NUMBER, orig.getLine(), orig.getColumn(), - String.valueOf(value), - new NumericValue(10, "" + value))} + Integer.toString(value), + new NumericValue(10, Integer.toString(value)))} ), true); } else { - push_source(new MacroTokenSource(m, args), true); + push_source(new MacroTokenSource(m, args, orig), true); } return true; @@ -875,10 +919,11 @@ private boolean macro(Macro m, Token orig) } /* processes a #define directive */ - private Token define() + private Token define(@Nonnull PreprocessorDirective preprocessorDirective) throws IOException, LexerException { Token tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); if (tok.getType() != IDENTIFIER) { error(tok, "Expected identifier"); return source_skipline(false); @@ -891,12 +936,14 @@ private Token define() return source_skipline(false); } - Macro m = new Macro(getSource(), name); + Macro m = new Macro(getSource(), name, tok.getLine(), tok.getColumn()); List args; tok = source_token(); if (tok.getType() == '(') { + preprocessorDirective.addToken(tok); tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); if (tok.getType() != ')') { args = new ArrayList(); ARGS: @@ -924,6 +971,7 @@ private Token define() return source_skipline(false); } tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); switch (tok.getType()) { case ',': break; @@ -950,6 +998,7 @@ private Token define() return source_skipline(false); } tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); } } else { assert tok.getType() == ')' : "Expected ')'"; @@ -970,6 +1019,7 @@ private Token define() /* Ensure no space at start. */ tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); EXPANSION: for (;;) { switch (tok.getType()) { @@ -1039,8 +1089,11 @@ private Token define() break; } tok = source_token(); + preprocessorDirective.addToken(tok); } + directive(preprocessorDirective); + if (getFeature(Feature.DEBUG)) System.err.println("Defined macro " + m); addMacro(m); @@ -1050,21 +1103,23 @@ private Token define() } @Nonnull - private Token undef() + private Token undef(@Nonnull PreprocessorDirective preprocessorDirective) throws IOException, LexerException { Token tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); if (tok.getType() != IDENTIFIER) { error(tok, "Expected identifier, not " + tok.getText()); if (tok.getType() == NL || tok.getType() == EOF) return tok; } else { - Macro m = macros.get(tok.getText()); + Macro m = getMacro(tok.getText()); if (m != null) { /* XXX error if predefined */ macros.remove(m.getName()); } + directive(preprocessorDirective); } return source_skipline(true); } @@ -1075,12 +1130,22 @@ private Token undef() * User code may override this method to implement a virtual * file system. */ - protected boolean include(@Nonnull VirtualFile file) + protected boolean include(@Nonnull VirtualFile file, int line) throws IOException, LexerException { // System.out.println("Try to include " + ((File)file).getAbsolutePath()); if (!file.isFile()) return false; + if (listener != null) { + if (getFeature(Feature.DEBUG)) + System.out.println("Trying to include " + file.getPath()); + if (listener.beforeInclude(file.getPath(), line)) { + // push_source() omitted + if (getFeature(Feature.DEBUG)) + System.err.println("pp: skipping " + file); + return true; + } + } if (getFeature(Feature.DEBUG)) System.err.println("pp: including " + file); push_source(file.getSource(), true); @@ -1090,12 +1155,12 @@ protected boolean include(@Nonnull VirtualFile file) /** * Includes a file from an include path, by name. */ - protected boolean include(@Nonnull Iterable path, @Nonnull String name) + protected boolean include(@Nonnull Iterable path, @Nonnull String name, int line) throws IOException, LexerException { for (String dir : path) { VirtualFile file = filesystem.getFile(dir, name); - if (include(file)) + if (include(file, line)) return true; } return false; @@ -1117,14 +1182,14 @@ private void include( } if (pdir != null) { VirtualFile ifile = pdir.getChildFile(name); - if (include(ifile)) + if (include(ifile, line)) return; } - if (include(quoteincludepath, name)) + if (include(quoteincludepath, name, line)) return; } - if (include(sysincludepath, name)) + if (include(sysincludepath, name, line)) return; StringBuilder buf = new StringBuilder(); @@ -1141,13 +1206,14 @@ private void include( } @Nonnull - private Token include(boolean next) + private Token include(boolean next, @Nonnull PreprocessorDirective preprocessorDirective) throws IOException, LexerException { LexerSource lexer = (LexerSource) source; try { lexer.setInclude(true); Token tok = token_nonwhite(); + preprocessorDirective.addToken(tok); String name; boolean quoted; @@ -1159,6 +1225,7 @@ private Token include(boolean next) HEADER: for (;;) { tok = token_nonwhite(); + preprocessorDirective.addToken(tok); switch (tok.getType()) { case STRING: buf.append((String) tok.getValue()); @@ -1190,7 +1257,7 @@ private Token include(boolean next) return source_skipline(false); } } - + directive(preprocessorDirective); /* Do the inclusion. */ include(source.getPath(), tok.getLine(), name, quoted, next); @@ -1228,7 +1295,7 @@ protected void pragma(@Nonnull Token name, @Nonnull List value) } @Nonnull - private Token pragma() + private Token pragma(@Nonnull PreprocessorDirective preprocessorDirective) throws IOException, LexerException { Token name; @@ -1236,6 +1303,7 @@ private Token pragma() NAME: for (;;) { Token tok = token(); + preprocessorDirective.addToken(tok); switch (tok.getType()) { case EOF: /* There ought to be a newline before EOF. @@ -1266,6 +1334,7 @@ private Token pragma() VALUE: for (;;) { tok = token(); + preprocessorDirective.addToken(tok); switch (tok.getType()) { case EOF: /* There ought to be a newline before EOF. @@ -1289,6 +1358,7 @@ private Token pragma() } } + directive(preprocessorDirective); pragma(name, value); return tok; /* The NL. */ @@ -1296,13 +1366,14 @@ private Token pragma() } /* For #error and #warning. */ - private void error(@Nonnull Token pptok, boolean is_error) + private void error(@Nonnull Token pptok, boolean is_error, @Nonnull PreprocessorDirective preprocessorDirective) throws IOException, LexerException { StringBuilder buf = new StringBuilder(); buf.append('#').append(pptok.getText()).append(' '); /* Peculiar construction to ditch first whitespace. */ Token tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); ERROR: for (;;) { switch (tok.getType()) { @@ -1314,11 +1385,14 @@ private void error(@Nonnull Token pptok, boolean is_error) break; } tok = source_token(); + preprocessorDirective.addToken(tok); } if (is_error) error(pptok, buf.toString()); else warning(pptok, buf.toString()); + + directive(preprocessorDirective); } /* This bypasses token() for #elif expressions. @@ -1332,7 +1406,7 @@ private Token expanded_token() Token tok = source_token(); // System.out.println("Source token is " + tok); if (tok.getType() == IDENTIFIER) { - Macro m = macros.get(tok.getText()); + Macro m = getMacro(tok.getText()); if (m == null) return tok; if (source.isExpanding(m)) @@ -1474,9 +1548,9 @@ private long expr(int priority) throws IOException, LexerException { /* - System.out.flush(); - (new Exception("expr(" + priority + ") called")).printStackTrace(); - System.err.flush(); + * System.out.flush(); + * (new Exception("expr(" + priority + ") called")).printStackTrace(); + * System.err.flush(); */ Token tok = expr_token(); @@ -1615,9 +1689,9 @@ private long expr(int priority) } /* - System.out.flush(); - (new Exception("expr returning " + lhs)).printStackTrace(); - System.err.flush(); + * System.out.flush(); + * (new Exception("expr returning " + lhs)).printStackTrace(); + * System.err.flush(); */ // System.out.println("expr returning " + lhs); return lhs; @@ -1675,6 +1749,9 @@ private Token _token() source.setActive(false); tok = source_token(); } finally { + if (source == null) { + throw new LexerException("Requested a token with a null Source"); + } /* XXX Tell lexer to stop ignoring warnings. */ source.setActive(true); } @@ -1783,7 +1860,7 @@ private Token _token() return tok; case IDENTIFIER: - Macro m = macros.get(tok.getText()); + Macro m = getMacro(tok.getText()); if (m == null) return tok; if (source.isExpanding(m)) @@ -1807,7 +1884,10 @@ private Token _token() // break; case HASH: + PreprocessorDirective preprocessorDirective = new PreprocessorDirective(); + preprocessorDirective.addToken(tok); tok = source_token_nonwhite(); + preprocessorDirective.addToken(tok); // (new Exception("here")).printStackTrace(); switch (tok.getType()) { case NL: @@ -1828,6 +1908,7 @@ private Token _token() + tok.getText()); return source_skipline(false); } + preprocessorDirective.setCommand(ppcmd); PP: switch (ppcmd) { @@ -1836,21 +1917,21 @@ private Token _token() if (!isActive()) return source_skipline(false); else - return define(); + return define(preprocessorDirective); // break; case PP_UNDEF: if (!isActive()) return source_skipline(false); else - return undef(); + return undef(preprocessorDirective); // break; case PP_INCLUDE: if (!isActive()) return source_skipline(false); else - return include(false); + return include(false, preprocessorDirective); // break; case PP_INCLUDE_NEXT: if (!isActive()) @@ -1861,7 +1942,7 @@ private Token _token() ); return source_skipline(false); } - return include(true); + return include(true, preprocessorDirective); // break; case PP_WARNING: @@ -1869,7 +1950,7 @@ private Token _token() if (!isActive()) return source_skipline(false); else - error(tok, ppcmd == PP_ERROR); + error(tok, ppcmd == PP_ERROR, preprocessorDirective); break; case PP_IF: @@ -1880,6 +1961,9 @@ private Token _token() expr_token = null; states.peek().setActive(expr(0) != 0); tok = expr_token(); /* unget */ + if (!states.peek().isActive()) + preprocessorDirective.setInactiveBlock(); + directive(preprocessorDirective); if (tok.getType() == NL) return tok; @@ -1899,6 +1983,8 @@ private Token _token() return source_skipline(false); } else if (state.isActive()) { /* The 'if' part got executed. */ + preprocessorDirective.setInactiveBlock(); + directive(preprocessorDirective); state.setParentActive(false); /* This is like # else # if but with * only one # end. */ @@ -1907,6 +1993,9 @@ private Token _token() } else { expr_token = null; state.setActive(expr(0) != 0); + if (!state.isActive()) + preprocessorDirective.setInactiveBlock(); + directive(preprocessorDirective); tok = expr_token(); /* unget */ if (tok.getType() == NL) @@ -1917,14 +2006,18 @@ private Token _token() case PP_ELSE: state = states.peek(); - if (false) - /* Check for 'if' */ ; else if (state.sawElse()) { + if (false) { + /* Check for 'if' */ ; + } else if (state.sawElse()) { error(tok, "#" + "else after #" + "else"); return source_skipline(false); } else { state.setSawElse(); state.setActive(!state.isActive()); + if (!state.isActive()) + preprocessorDirective.setInactiveBlock(); + directive(preprocessorDirective); return source_skipline(warnings.contains(Warning.ENDIF_LABELS)); } // break; @@ -1940,12 +2033,17 @@ private Token _token() error(tok, "Expected identifier, not " + tok.getText()); + directive(preprocessorDirective); return source_skipline(false); } else { + preprocessorDirective.addToken(tok); String text = tok.getText(); boolean exists = macros.containsKey(text); states.peek().setActive(exists); + if (!exists) + preprocessorDirective.setInactiveBlock(); + directive(preprocessorDirective); return source_skipline(true); } } @@ -1961,12 +2059,17 @@ private Token _token() error(tok, "Expected identifier, not " + tok.getText()); + directive(preprocessorDirective); return source_skipline(false); } else { + preprocessorDirective.addToken(tok); String text = tok.getText(); boolean exists = macros.containsKey(text); states.peek().setActive(!exists); + if (exists) + preprocessorDirective.setInactiveBlock(); + directive(preprocessorDirective); return source_skipline(true); } } @@ -1974,17 +2077,19 @@ private Token _token() case PP_ENDIF: pop_state(); + directive(preprocessorDirective); return source_skipline(warnings.contains(Warning.ENDIF_LABELS)); // break; case PP_LINE: + directive(preprocessorDirective); return source_skipline(false); // break; case PP_PRAGMA: if (!isActive()) return source_skipline(false); - return pragma(); + return pragma(preprocessorDirective); // break; default: @@ -2050,7 +2155,7 @@ public String toString() { Iterator mt = keys.iterator(); while (mt.hasNext()) { String key = mt.next(); - Macro macro = macros.get(key); + Macro macro = getMacro(key); buf.append("#").append("macro ").append(macro).append("\n"); } diff --git a/src/main/java/org/anarres/cpp/PreprocessorDirective.java b/src/main/java/org/anarres/cpp/PreprocessorDirective.java new file mode 100644 index 0000000..1bb0dea --- /dev/null +++ b/src/main/java/org/anarres/cpp/PreprocessorDirective.java @@ -0,0 +1,57 @@ +package org.anarres.cpp; + +import javax.annotation.Nonnull; +import java.lang.String; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents all the tokens that form a single preprocessor directive. + * Additionally informs if the block after + */ +public class PreprocessorDirective { + private final List directiveTokens = new ArrayList<>(); + private boolean activeBlock = true; + private PreprocessorCommand command; + + public PreprocessorDirective() { + } + + public void addToken(@Nonnull Token token) { + directiveTokens.add(token); + } + + public void setCommand(PreprocessorCommand cmd) { + this.command = cmd; + } + + public PreprocessorCommand getCommand() { + return this.command; + } + + @Nonnull + public List getTokenList() { return this.directiveTokens; } + + public boolean isActiveBlock() { + return this.activeBlock; + } + + public void setInactiveBlock() { + this.activeBlock = false; + } + + @Override + public String toString() { + String result = new String(); + if (!activeBlock) + result += "inactive block "; + for (Token tok : directiveTokens) { + if (tok.getType() == Token.NL) { + result += " \\n"; + } else { + result += " " + tok.getText(); + } + } + return result; + } +} diff --git a/src/main/java/org/anarres/cpp/PreprocessorListener.java b/src/main/java/org/anarres/cpp/PreprocessorListener.java index a5b4339..cf2757b 100644 --- a/src/main/java/org/anarres/cpp/PreprocessorListener.java +++ b/src/main/java/org/anarres/cpp/PreprocessorListener.java @@ -23,31 +23,7 @@ * error and warning events will throw an exception. Installing a * listener allows more intelligent handling of these events. */ -public class PreprocessorListener { - - private int errors; - private int warnings; - - public PreprocessorListener() { - clear(); - } - - public void clear() { - errors = 0; - warnings = 0; - } - - public int getErrors() { - return errors; - } - - public int getWarnings() { - return warnings; - } - - protected void print(String msg) { - System.err.println(msg); - } +public interface PreprocessorListener { /** * Handles a warning. @@ -58,11 +34,7 @@ protected void print(String msg) { */ public void handleWarning(Source source, int line, int column, String msg) - throws LexerException { - warnings++; - print(source.getName() + ":" + line + ":" + column - + ": warning: " + msg); - } + throws LexerException; /** * Handles an error. @@ -73,13 +45,30 @@ public void handleWarning(Source source, int line, int column, */ public void handleError(Source source, int line, int column, String msg) - throws LexerException { - errors++; - print(source.getName() + ":" + line + ":" + column - + ": error: " + msg); - } + throws LexerException; + + public void handleSourceChange(Source source, String event); - public void handleSourceChange(Source source, String event) { - } + /** + * Called when preprocessor is about to include file. + * + * @param filePath file path + * @param line line of include directive + * @return true if specified file should be skipped, + * false otherwise + */ + public boolean beforeInclude(String filePath, int line); + + public void handlePreprocesorDirective(Source source, PreprocessorDirective directive); + + /** + * @param definitionFileName Can be null in certain cases. + * @param definitionLine Macro.UNKNOWN_POSITION_VALUE if it is + * unknown, otherwise proper value + * @param definitionColumn Macro.UNKNOWN_POSITION_VALUE if it + * is unknown, otherwise proper value + */ + public void handleMacroExpansion(Source source, int line, int column, String macro, + String definitionFileName, int definitionLine, int definitionColumn); } diff --git a/src/main/java/org/anarres/cpp/Source.java b/src/main/java/org/anarres/cpp/Source.java index ef20803..e6df101 100644 --- a/src/main/java/org/anarres/cpp/Source.java +++ b/src/main/java/org/anarres/cpp/Source.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.Iterator; import javax.annotation.CheckForNull; +import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import static org.anarres.cpp.Token.*; @@ -129,7 +130,7 @@ public void setListener(PreprocessorListener pl) { * If no Source on the stack is a FileLexerSource, returns null. */ @CheckForNull - /* pp */ String getPath() { + public String getPath() { Source parent = getParent(); if (parent != null) return parent.getPath(); @@ -139,7 +140,8 @@ public void setListener(PreprocessorListener pl) { /** * Returns the human-readable name of the current Source. */ - /* pp */ String getName() { + @CheckForNull + public String getName() { Source parent = getParent(); if (parent != null) return parent.getName(); @@ -149,6 +151,7 @@ public void setListener(PreprocessorListener pl) { /** * Returns the current line number within this Source. */ + @Nonnegative public int getLine() { Source parent = getParent(); if (parent == null) @@ -166,6 +169,42 @@ public int getColumn() { return parent.getColumn(); } + /** + * @return Source object that represents the root of the macro expansion + * if it is currently in progress. If no macro expansion is + * currently in progress, returns null. + */ + MacroTokenSource getExpandingRoot() { + final Source parent = getParent(); + if (parent == null) { + return null; + } + return parent.getExpandingRoot(); + } + + /** + * @return Token with a macro identifier in the source code (not from + * a macro definition) that caused appearance of this source. + * If no macro is currently being expanded, null is returned. + */ + final Token getExpandingRootToken() { + final MacroTokenSource expandingRoot = getExpandingRoot(); + if (expandingRoot != null) { + return expandingRoot.getOriginalToken(); + } + return null; + } + + /*** + * Returns true if this Source is expanding any macro. + */ + boolean isExpanding() { + Source parent = getParent(); + if (parent != null) + return parent.isExpanding(); + return false; + } + /** * Returns true if this Source is expanding the given macro. * diff --git a/src/main/java/org/anarres/cpp/Token.java b/src/main/java/org/anarres/cpp/Token.java index 3e6eb3e..5cdf229 100644 --- a/src/main/java/org/anarres/cpp/Token.java +++ b/src/main/java/org/anarres/cpp/Token.java @@ -21,22 +21,46 @@ * * @see Preprocessor */ -public final class Token { +public final class Token implements Cloneable { // public static final int EOF = -1; private final int type; + private final int expected_type; private int line; private int column; private final Object value; private final String text; + /** + * Indicates that token comes from macro expansion. + */ + private boolean isExpanded; + /** + * Token that this one replaced (because of a macro expansion). + * It can be null. + */ + private Token originalMacroToken; + /** + * True if and only if the token comes from a macro argument stringizing, + * e.g. as the result of use of a macro defined as follows: + * #define STRINGIZE(x) #x + */ + private boolean isStringized; - public Token(int type, int line, int column, + public Token(int type, int expected, int line, int column, String text, Object value) { this.type = type; + this.expected_type = expected; this.line = line; this.column = column; this.text = text; this.value = value; + this.isExpanded = false; + this.isStringized = false; + } + + public Token(int type, int line, int column, + String text, Object value) { + this(type, -1, line, column, text, value); } public Token(int type, int line, int column, String text) { @@ -47,14 +71,26 @@ public Token(int type, int line, int column, String text) { this(type, -1, -1, text, value); } + Token(int type, int expected, String text, Object value) { + this(type, expected, -1, -1, text, value); + } + /* pp */ Token(int type, String text) { this(type, text, null); } + Token(int type, int expected, String text) { + this(type, expected, text, null); + } + /* pp */ Token(int type) { this(type, TokenType.getTokenText(type)); } + Token(int type, int expected) { + this(type, expected, TokenType.getTokenText(type)); + } + /** * Returns the semantic type of this token. */ @@ -62,6 +98,13 @@ public int getType() { return type; } + /** + * If the token type is INVALID returns the expected type of this token. + */ + public int getExpectedType() { + return expected_type; + } + /* pp */ void setLocation(int line, int column) { this.line = line; this.column = column; @@ -109,6 +152,65 @@ public Object getValue() { return value; } + /** + * Returns if token comes from macro expansion. + * + * @return true when macro comes from macro expansion + */ + public boolean isExpanded() { + return isExpanded; + } + + /** + * @return True if and only if the token comes from a use of operator '#' + * in a macro definition. + */ + public boolean isStringized() { + return isStringized; + } + + /** + * @return Token that has been replaced by this one (and possibly some other + * ones) because of macro expansion. It can be null even if a macro + * expansion has happened. + * If it is not null, it is never a token from a macro definition. + */ + public Token getOriginalMacroToken() { + return originalMacroToken; + } + + /* pp */ void setExpanded(boolean isExpanded) { + this.isExpanded = isExpanded; + } + + /** + * Sets the isStringized flag to the given value. + * + * @param isStringized Value of the isStringized flag to set. + */ + void setStringized(boolean isStringized) { + this.isStringized = isStringized; + } + + /** + * Unconditionally sets the original macro token of this object to the given + * one. + * + * @param originalMacroToken Original macro token of this object. It can be null. + */ + void setOriginalMacroToken(Token originalMacroToken) { + this.originalMacroToken = originalMacroToken; + } + + /** + * @return Shallow copy of this token (all references in the returned object + * are the same as in this one). + */ + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + /** * Returns a description of this token, for debugging purposes. */ @@ -122,6 +224,8 @@ public String toString() { if (column != -1) buf.append(',').append(column); } + if (isExpanded) + buf.append("(expanded)"); buf.append("]:"); if (text != null) buf.append('"').append(text).append('"'); diff --git a/src/test/java/org/anarres/cpp/ErrorTest.java b/src/test/java/org/anarres/cpp/ErrorTest.java index 8777452..42240d4 100644 --- a/src/test/java/org/anarres/cpp/ErrorTest.java +++ b/src/test/java/org/anarres/cpp/ErrorTest.java @@ -22,7 +22,7 @@ private boolean testError(Preprocessor p) private void testError(String input) throws Exception { StringLexerSource sl; - PreprocessorListener pl; + DefaultPreprocessorListener pl; Preprocessor p; /* Without a PreprocessorListener, throws an exception. */ @@ -42,7 +42,7 @@ private void testError(String input) throws Exception { p = new Preprocessor(); p.addFeature(Feature.CSYNTAX); p.addInput(sl); - pl = new PreprocessorListener(); + pl = new DefaultPreprocessorListener(); p.setListener(pl); assertNotNull("CPP has listener", p.getListener()); assertTrue(testError(p)); diff --git a/src/test/java/org/anarres/cpp/IsExpandedFlagTest.java b/src/test/java/org/anarres/cpp/IsExpandedFlagTest.java new file mode 100644 index 0000000..292e422 --- /dev/null +++ b/src/test/java/org/anarres/cpp/IsExpandedFlagTest.java @@ -0,0 +1,128 @@ +package org.anarres.cpp; + +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Grzegorz Kołakowski + */ +public class IsExpandedFlagTest { + + private Preprocessor preprocessor; + + @Before + public void setUp() throws Exception { + preprocessor = new Preprocessor(); + } + + @Test + public void testSimpleExpansion() throws Exception { + final List tokens = getTokens("is_expanded_flag/simple_expansion.c"); + System.out.println(tokens); + + assertEquals(10, tokens.size()); + + for (int i = 0; i < 10; ++i) { + assertEquals(tokens.get(i).toString(), i == 7, tokens.get(i).isExpanded()); + } + } + + @Test + public void testPredefinedMacroExpansion() throws Exception { + preprocessor.addMacro("PREDEFINED", "123"); + final List tokens = getTokens("is_expanded_flag/predefined_macro_expansion.c"); + System.out.println(tokens); + + assertEquals(9, tokens.size()); + + for (int i = 0; i < 9; ++i) { + assertEquals(tokens.get(i).toString(), i == 6, tokens.get(i).isExpanded()); + } + } + + @Test + public void testExpansionWithArgs() throws Exception { + final List tokens = getTokens("is_expanded_flag/expansion_with_args.c"); + System.out.println(tokens); + + assertEquals(26, tokens.size()); + + for (Token token : tokens) { + switch (token.getType()) { + case ':': + case '?': + case '>': + assertTrue(token.toString(), token.isExpanded()); + break; + case Token.WHITESPACE: + case Token.NL: + case Token.EOF: + // ignore whitespaces, eof + break; + default: + assertFalse(token.toString(), token.isExpanded()); + break; + } + } + } + + @Test + public void testNestedExpansion() throws Exception { + final List tokens = getTokens("is_expanded_flag/nested_expansion.c"); + System.out.println(tokens); + + assertEquals(32, tokens.size()); + + for (Token token : tokens) { + final String text = token.getText(); + switch (token.getType()) { + case '%': + case Token.EQ: + case Token.LAND: + assertTrue(token.toString(), token.isExpanded()); + break; + case Token.WHITESPACE: + case Token.NL: + case Token.EOF: + // ignore whitespaces, eof + break; + case Token.NUMBER: + if ("0".equals(text)) { + assertTrue(token.toString(), token.isExpanded()); + } else if ("3423".equals(text)) { + assertFalse(token.toString(), token.isExpanded()); + } + break; + default: + assertFalse(token.toString(), token.isExpanded()); + break; + } + } + } + + private List getTokens(String resourcePath) throws IOException, LexerException { + final List result = new ArrayList<>(); + final String filePath = Thread.currentThread() + .getContextClassLoader() + .getResource(resourcePath) + .getFile(); + preprocessor.addInput(new File(filePath)); + + Token token = preprocessor.token(); + while (token.getType() != Token.EOF) { + result.add(token); + token = preprocessor.token(); + } + return result; + } + +} diff --git a/src/test/java/org/anarres/cpp/LexerSourceTest.java b/src/test/java/org/anarres/cpp/LexerSourceTest.java index f51b253..db90b31 100644 --- a/src/test/java/org/anarres/cpp/LexerSourceTest.java +++ b/src/test/java/org/anarres/cpp/LexerSourceTest.java @@ -70,6 +70,7 @@ public void testLexerSource() testLexerSource("/**/", true, CCOMMENT); testLexerSource("/* /**/ */", true, CCOMMENT, WHITESPACE, '*', '/'); testLexerSource("/** ** **/", true, CCOMMENT); + testLexerSource("/*", true, INVALID); testLexerSource("//* ** **/", true, CPPCOMMENT); testLexerSource("'\\r' '\\xf' '\\xff' 'x' 'aa' ''", true, CHARACTER, WHITESPACE, @@ -98,5 +99,6 @@ public void testNumbers() throws Exception { testLexerSource("1e6", true, NUMBER); testLexerSource("1.45e6", true, NUMBER); testLexerSource(".45e6", true, NUMBER); + testLexerSource("-6", true, '-', NUMBER); } } diff --git a/src/test/java/org/anarres/cpp/NumericValueTest.java b/src/test/java/org/anarres/cpp/NumericValueTest.java new file mode 100644 index 0000000..5668452 --- /dev/null +++ b/src/test/java/org/anarres/cpp/NumericValueTest.java @@ -0,0 +1,97 @@ +package org.anarres.cpp; + +import java.io.IOException; +import org.junit.Test; +import static org.anarres.cpp.Token.*; +import static org.junit.Assert.*; + +/** + * + * @author shevek + */ +public class NumericValueTest { + + private Token testNumericValue(String in) throws IOException, LexerException { + StringLexerSource s = new StringLexerSource(in); + + Token tok = s.token(); + System.out.println("Token is " + tok); + assertEquals(NUMBER, tok.getType()); + + Token eof = s.token(); + assertEquals("Didn't get EOF, but " + tok, EOF, eof.getType()); + + return tok; + } + + private void testNumericValue(String in, double out) throws IOException, LexerException { + System.out.println("Testing '" + in + "' -> " + out); + Token tok = testNumericValue(in); + assertEquals(in, tok.getText()); + NumericValue value = (NumericValue) tok.getValue(); + assertEquals("Double mismatch", out, value.doubleValue(), 0.01d); + assertEquals("Float mismatch", (float) out, value.floatValue(), 0.01f); + assertEquals("Long mismatch", (long) out, value.longValue()); + assertEquals("Integer mismatch", (int) out, value.intValue()); + } + + @Test + public void testNumericValue() throws Exception { + + // Zero + testNumericValue("0", 0); + + // Decimal + testNumericValue("1", 1); + testNumericValue("1L", 1); + testNumericValue("12", 12); + testNumericValue("12L", 12); + + // Hex + testNumericValue("0xf", 0xf); + testNumericValue("0xfL", 0xf); + testNumericValue("0x12", 0x12); + testNumericValue("0x12L", 0x12); + + // Negative + // testNumericValue("-0", 0); + // testNumericValue("-1", -1); + + // Negative hex + // testNumericValue("-0x56", -0x56); + // testNumericValue("-0x102", -0x102); + + // Octal and negative octal + testNumericValue("0673", Integer.parseInt("673", 8)); + // testNumericValue("-0673", Integer.parseInt("-673", 8)); + + // Floating point + testNumericValue(".0", 0); + testNumericValue(".00", 0); + testNumericValue("0.", 0); + testNumericValue("0.0", 0); + testNumericValue("00.0", 0); + testNumericValue("00.", 0); + + // Sign on exponents + testNumericValue("1e1", 1e1); + // testNumericValue("-1e1", -1e1); + testNumericValue("1e-1", 1e-1); + + // Hex numbers with decimal exponents + testNumericValue("0x12e3", 0x12e3); + testNumericValue("0x12p3", 0x12p3); + + // Octal numbers with decimal exponents + testNumericValue("012e3", 012e3); // Fails + testNumericValue("067e4", 067e4); // Fails + + // Issues a warning. + try { + testNumericValue("097", 97); + fail("No warning."); + } catch (LexerException e) { + } + + } +} diff --git a/src/test/resources/is_expanded_flag/expansion_with_args.c b/src/test/resources/is_expanded_flag/expansion_with_args.c new file mode 100644 index 0000000..f479c35 --- /dev/null +++ b/src/test/resources/is_expanded_flag/expansion_with_args.c @@ -0,0 +1,3 @@ +#define MAX(a, b) a > b ? a : b + +int i = MAX(123, 321) + 1; diff --git a/src/test/resources/is_expanded_flag/nested_expansion.c b/src/test/resources/is_expanded_flag/nested_expansion.c new file mode 100644 index 0000000..526314c --- /dev/null +++ b/src/test/resources/is_expanded_flag/nested_expansion.c @@ -0,0 +1,5 @@ +#define DIVISIBLE2(n) n % 2 == 0 +#define DIVISIBLE3(n) n % 3 == 0 +#define DIVISIBLE6(n) DIVISIBLE2(n) && DIVISIBLE3(n) + +bool isDivisible = DIVISIBLE6(3423); diff --git a/src/test/resources/is_expanded_flag/predefined_macro_expansion.c b/src/test/resources/is_expanded_flag/predefined_macro_expansion.c new file mode 100644 index 0000000..dc50d96 --- /dev/null +++ b/src/test/resources/is_expanded_flag/predefined_macro_expansion.c @@ -0,0 +1 @@ +int i = PREDEFINED; diff --git a/src/test/resources/is_expanded_flag/simple_expansion.c b/src/test/resources/is_expanded_flag/simple_expansion.c new file mode 100644 index 0000000..489ef3e --- /dev/null +++ b/src/test/resources/is_expanded_flag/simple_expansion.c @@ -0,0 +1,3 @@ +#define ONE_HUNDRED 100 + +int i = ONE_HUNDRED;