summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--build.gradle66
-rw-r--r--build.xml46
-rw-r--r--buildSrc/build.gradle12
-rw-r--r--buildSrc/src/main/groovy/VelocityPlugin.groovy33
-rw-r--r--buildSrc/src/main/groovy/VelocityTask.groovy58
-rw-r--r--codequality/HEADER13
-rw-r--r--codequality/checkstyle.xml188
-rw-r--r--etc/MANIFEST2
-rw-r--r--etc/build.properties64
-rw-r--r--etc/checkstyle/config.xml178
-rw-r--r--etc/global.xml108
-rw-r--r--etc/javadoc/.gitignore0
-rw-r--r--etc/junit/xsl/junit-frames.xsl716
-rw-r--r--etc/junit/xsl/junit-noframes.xsl461
-rw-r--r--etc/targets/global-checkstyle.xml23
-rw-r--r--etc/targets/global-clean.xml8
-rw-r--r--etc/targets/global-cobertura.xml35
-rw-r--r--etc/targets/global-compile.xml35
-rw-r--r--etc/targets/global-findbugs.xml47
-rw-r--r--etc/targets/global-inject.xml20
-rw-r--r--etc/targets/global-jar.xml40
-rw-r--r--etc/targets/global-javadoc.xml55
-rw-r--r--etc/targets/global-junit.xml99
-rw-r--r--etc/targets/global-tar.xml86
-rw-r--r--etc/targets/global-taskdefs.xml65
-rw-r--r--etc/targets/global-verify.xml23
-rw-r--r--etc/targets/global-vpp.xml32
-rw-r--r--gradle.properties1
-rw-r--r--gradle/buildscript.gradle11
-rw-r--r--gradle/check.gradle25
-rw-r--r--gradle/convention.gradle88
-rw-r--r--gradle/license.gradle8
-rw-r--r--gradle/maven.gradle69
-rw-r--r--gradle/netflix-oss.gradle1
-rw-r--r--gradle/release.gradle60
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 50557 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew164
-rw-r--r--gradlew.bat90
-rw-r--r--settings.gradle1
-rw-r--r--src/docs/javadoc/.gitignore0
-rw-r--r--src/java/org/anarres/cpp/ChrootFileSystem.java79
-rw-r--r--src/java/org/anarres/cpp/CppReader.java153
-rw-r--r--src/java/org/anarres/cpp/CppTask.java226
-rw-r--r--src/java/org/anarres/cpp/FixedTokenSource.java65
-rw-r--r--src/java/org/anarres/cpp/JavaFileSystem.java80
-rw-r--r--src/java/org/anarres/cpp/JoinReader.java209
-rw-r--r--src/java/org/anarres/cpp/LexerSource.java893
-rw-r--r--src/java/org/anarres/cpp/MacroTokenSource.java208
-rw-r--r--src/java/org/anarres/cpp/Main.java410
-rw-r--r--src/java/org/anarres/cpp/NumericValue.java179
-rw-r--r--src/java/org/anarres/cpp/Preprocessor.java1983
-rw-r--r--src/java/org/anarres/cpp/PreprocessorListener.java88
-rw-r--r--src/java/org/anarres/cpp/Source.java293
-rw-r--r--src/java/org/anarres/cpp/Token.java213
-rw-r--r--src/main/java/org/anarres/cpp/Argument.java (renamed from src/java/org/anarres/cpp/Argument.java)0
-rw-r--r--src/main/java/org/anarres/cpp/ChrootFileSystem.java84
-rw-r--r--src/main/java/org/anarres/cpp/CppReader.java153
-rw-r--r--src/main/java/org/anarres/cpp/CppTask.java215
-rw-r--r--src/main/java/org/anarres/cpp/Feature.java (renamed from src/java/org/anarres/cpp/Feature.java)0
-rw-r--r--src/main/java/org/anarres/cpp/FileLexerSource.java (renamed from src/java/org/anarres/cpp/FileLexerSource.java)0
-rw-r--r--src/main/java/org/anarres/cpp/FixedTokenSource.java60
-rw-r--r--src/main/java/org/anarres/cpp/InputLexerSource.java (renamed from src/java/org/anarres/cpp/InputLexerSource.java)62
-rw-r--r--src/main/java/org/anarres/cpp/InternalException.java (renamed from src/java/org/anarres/cpp/InternalException.java)0
-rw-r--r--src/main/java/org/anarres/cpp/JavaFileSystem.java84
-rw-r--r--src/main/java/org/anarres/cpp/JoinReader.java218
-rw-r--r--src/main/java/org/anarres/cpp/LexerException.java (renamed from src/java/org/anarres/cpp/LexerException.java)0
-rw-r--r--src/main/java/org/anarres/cpp/LexerSource.java910
-rw-r--r--src/main/java/org/anarres/cpp/Macro.java (renamed from src/java/org/anarres/cpp/Macro.java)1
-rw-r--r--src/main/java/org/anarres/cpp/MacroTokenSource.java203
-rw-r--r--src/main/java/org/anarres/cpp/Main.java320
-rw-r--r--src/main/java/org/anarres/cpp/NumericValue.java179
-rw-r--r--src/main/java/org/anarres/cpp/Preprocessor.java2016
-rw-r--r--src/main/java/org/anarres/cpp/PreprocessorCommand.java44
-rw-r--r--src/main/java/org/anarres/cpp/PreprocessorListener.java85
-rw-r--r--src/main/java/org/anarres/cpp/ResourceFileSystem.java81
-rw-r--r--src/main/java/org/anarres/cpp/Source.java287
-rw-r--r--src/main/java/org/anarres/cpp/SourceIterator.java (renamed from src/java/org/anarres/cpp/SourceIterator.java)0
-rw-r--r--src/main/java/org/anarres/cpp/State.java (renamed from src/java/org/anarres/cpp/State.java)0
-rw-r--r--src/main/java/org/anarres/cpp/StringLexerSource.java (renamed from src/java/org/anarres/cpp/StringLexerSource.java)55
-rw-r--r--src/main/java/org/anarres/cpp/Token.java193
-rw-r--r--src/main/java/org/anarres/cpp/TokenSnifferSource.java (renamed from src/java/org/anarres/cpp/TokenSnifferSource.java)0
-rw-r--r--src/main/java/org/anarres/cpp/TokenType.java128
-rw-r--r--src/main/java/org/anarres/cpp/VirtualFile.java (renamed from src/java/org/anarres/cpp/VirtualFile.java)28
-rw-r--r--src/main/java/org/anarres/cpp/VirtualFileSystem.java (renamed from src/java/org/anarres/cpp/VirtualFileSystem.java)7
-rw-r--r--src/main/java/org/anarres/cpp/Warning.java (renamed from src/java/org/anarres/cpp/Warning.java)16
-rw-r--r--src/main/resources/log4j.properties (renamed from src/resources/log4j.properties)0
-rw-r--r--src/main/resources/org/anarres/cpp/taskdef.properties (renamed from src/resources/org/anarres/cpp/taskdef.properties)0
-rw-r--r--src/main/velocity/org/anarres/cpp/Version.java (renamed from src/java/org/anarres/cpp/Version.java)11
-rw-r--r--src/test/java/org/anarres/cpp/CppReaderTest.java34
-rw-r--r--src/test/java/org/anarres/cpp/ErrorTest.java65
-rw-r--r--src/test/java/org/anarres/cpp/JavaFileSystemTest.java38
-rw-r--r--src/test/java/org/anarres/cpp/JoinReaderTest.java41
-rw-r--r--src/test/java/org/anarres/cpp/LexerSourceTest.java86
-rw-r--r--src/test/java/org/anarres/cpp/MainTest.java11
-rw-r--r--src/test/java/org/anarres/cpp/PreprocessorTest.java167
-rw-r--r--src/test/resources/test0.c (renamed from src/input/test0.c)0
-rw-r--r--src/test/resources/test0.h (renamed from src/input/test0.h)0
-rw-r--r--src/test/resources/test1.c (renamed from src/input/test1.c)0
-rw-r--r--src/test/resources/test1.h (renamed from src/input/test1.h)0
-rw-r--r--src/test/resources/trigraph.c (renamed from src/input/trigraph.c)0
-rw-r--r--src/tests/AutoTestSuite.java121
-rw-r--r--src/tests/org/anarres/cpp/BaseTestCase.java6
-rw-r--r--src/tests/org/anarres/cpp/CppReaderTestCase.java34
-rw-r--r--src/tests/org/anarres/cpp/ErrorTestCase.java66
-rw-r--r--src/tests/org/anarres/cpp/JavaFileSystemTestCase.java39
-rw-r--r--src/tests/org/anarres/cpp/JoinReaderTestCase.java40
-rw-r--r--src/tests/org/anarres/cpp/LexerSourceTestCase.java87
-rw-r--r--src/tests/org/anarres/cpp/MainTestCase.java15
-rw-r--r--src/tests/org/anarres/cpp/PreprocessorTestCase.java160
111 files changed, 6690 insertions, 7877 deletions
diff --git a/.gitignore b/.gitignore
index b86d665..a8b6931 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
build
.*.swp
+.gradle
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..df4f9d3
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,66 @@
+// 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.${githubProjectName}"
+
+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/build.xml b/build.xml
deleted file mode 100644
index 4657aa1..0000000
--- a/build.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="jcpp" default="all" basedir=".">
- <dirname property="global.dir.root" file="${ant.file}"/>
- <property name="global.dir.etc" value="${global.dir.root}/etc"/>
- <import file="${global.dir.etc}/global.xml" />
-
- <target name="clean" depends="global-clean" />
- <target name="compile" depends="global-compile" />
- <target name="all" depends="global-tar" />
-
-
- <target name="junit" depends="global-junit" />
- <target name="test" depends="global-junit" />
- <!-- global-findbugs" -->
-
- <target name="findbugs" depends="global-findbugs,global-findbugs-gui" />
-
- <target name="test-ant">
- <delete dir="${global.dir.build}/output" />
- <mkdir dir="${global.dir.build}/output" />
-
- <path id="cpp-classpath">
- <path refid="runtime-classpath" />
- <pathelement path="${global.dir.src.resources}" />
- </path>
-
- <taskdef
- resource="org/anarres/cpp/taskdef.properties"
- classpathref="cpp-classpath"
- loaderref="ant" />
-
- <cpp todir="${global.dir.build}/output">
- <fileset file="${global.dir.src}/input/test0.c" />
- <fileset file="${global.dir.src}/input/test1.c" />
- <systemincludepath>
- <pathelement path="${global.dir.src}/input" />
- </systemincludepath>
- <localincludepath>
- <pathelement path="${global.dir.src}/input" />
- </localincludepath>
- </cpp>
- </target>
-
-
-</project>
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 0000000..324859d
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,12 @@
+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
new file mode 100644
index 0000000..cc8741a
--- /dev/null
+++ b/buildSrc/src/main/groovy/VelocityPlugin.groovy
@@ -0,0 +1,33 @@
+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<String, Object> contextValues = [:]
+ def context(Closure closure) {
+ contextValues.with closure
+ }
+}
+
+class VelocityPlugin implements Plugin<Project> {
+ 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
new file mode 100644
index 0000000..6a36903
--- /dev/null
+++ b/buildSrc/src/main/groovy/VelocityTask.groovy
@@ -0,0 +1,58 @@
+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<String, Object> 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
new file mode 100644
index 0000000..3102e4b
--- /dev/null
+++ b/codequality/HEADER
@@ -0,0 +1,13 @@
+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
new file mode 100644
index 0000000..47c01a2
--- /dev/null
+++ b/codequality/checkstyle.xml
@@ -0,0 +1,188 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+
+<module name="Checker">
+
+ <!-- Checks that a package-info.java file exists for each package. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
+ <!--
+ <module name="JavadocPackage">
+ <property name="allowLegacy" value="true"/>
+ </module>
+ -->
+
+ <!-- Checks whether files end with a new line. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+ <module name="NewlineAtEndOfFile"/>
+
+ <!-- Checks that property files contain the same keys. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+ <module name="Translation"/>
+
+ <!-- Checks for Size Violations. -->
+ <!-- See http://checkstyle.sf.net/config_sizes.html -->
+ <module name="FileLength"/>
+
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="FileTabCharacter"/>
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html -->
+ <module name="RegexpSingleline">
+ <property name="format" value="\s+$"/>
+ <property name="minimum" value="0"/>
+ <property name="maximum" value="0"/>
+ <property name="message" value="Line has trailing spaces."/>
+ <property name="severity" value="info"/>
+ </module>
+
+ <module name="TreeWalker">
+
+ <!-- Checks for Javadoc comments. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+ <module name="JavadocMethod">
+ <property name="scope" value="package"/>
+ <property name="allowMissingParamTags" value="true"/>
+ <property name="allowMissingThrowsTags" value="true"/>
+ <property name="allowMissingReturnTag" value="true"/>
+ <property name="allowThrowsTagsForSubclasses" value="true"/>
+ <property name="allowUndeclaredRTE" value="true"/>
+ <property name="allowMissingPropertyJavadoc" value="true"/>
+ </module>
+ <module name="JavadocType">
+ <property name="scope" value="package"/>
+ </module>
+ <module name="JavadocVariable">
+ <property name="scope" value="package"/>
+ </module>
+ <module name="JavadocStyle">
+ <property name="checkEmptyJavadoc" value="true"/>
+ </module>
+
+ <!-- Checks for Naming Conventions. -->
+ <!-- See http://checkstyle.sf.net/config_naming.html -->
+ <module name="ConstantName"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="MethodName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+
+ <!-- Checks for imports -->
+ <!-- See http://checkstyle.sf.net/config_import.html -->
+ <module name="AvoidStarImport"/>
+ <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+ <module name="RedundantImport"/>
+ <module name="UnusedImports"/>
+
+
+ <!-- Checks for Size Violations. -->
+ <!-- See http://checkstyle.sf.net/config_sizes.html -->
+ <module name="LineLength">
+ <!-- what is a good max value? -->
+ <property name="max" value="120"/>
+ <!-- ignore lines like "$File: //depot/... $" -->
+ <property name="ignorePattern" value="\$File.*\$"/>
+ <property name="severity" value="info"/>
+ </module>
+ <module name="MethodLength"/>
+ <module name="ParameterNumber"/>
+
+
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="EmptyForIteratorPad"/>
+ <module name="GenericWhitespace"/>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="OperatorWrap"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+
+ <!-- Modifier Checks -->
+ <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+ <module name="ModifierOrder"/>
+ <module name="RedundantModifier"/>
+
+
+ <!-- Checks for blocks. You know, those {}'s -->
+ <!-- See http://checkstyle.sf.net/config_blocks.html -->
+ <module name="AvoidNestedBlocks"/>
+ <module name="EmptyBlock">
+ <property name="option" value="text"/>
+ </module>
+ <module name="LeftCurly"/>
+ <module name="NeedBraces"/>
+ <module name="RightCurly"/>
+
+
+ <!-- Checks for common coding problems -->
+ <!-- See http://checkstyle.sf.net/config_coding.html -->
+ <!-- <module name="AvoidInlineConditionals"/> -->
+ <module name="EmptyStatement"/>
+ <module name="EqualsHashCode"/>
+ <module name="HiddenField">
+ <property name="ignoreConstructorParameter" value="true"/>
+ <property name="ignoreSetter" value="true"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="IllegalInstantiation"/>
+ <module name="InnerAssignment"/>
+ <module name="MagicNumber">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="MissingSwitchDefault"/>
+ <!-- Problem with finding exception types... -->
+ <module name="RedundantThrows">
+ <property name="allowUnchecked" value="true"/>
+ <property name="suppressLoadErrors" value="true"/>
+ <property name="severity" value="info"/>
+ </module>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+
+ <!-- Checks for class design -->
+ <!-- See http://checkstyle.sf.net/config_design.html -->
+ <!-- <module name="DesignForExtension"/> -->
+ <module name="FinalClass"/>
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InterfaceIsType"/>
+ <module name="VisibilityModifier"/>
+
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html -->
+ <module name="ArrayTypeStyle"/>
+ <!-- <module name="FinalParameters"/> -->
+ <module name="TodoComment">
+ <property name="format" value="TODO"/>
+ <property name="severity" value="info"/>
+ </module>
+ <module name="UpperEll"/>
+
+ <module name="FileContentsHolder"/> <!-- Required by comment suppression filters -->
+
+ </module>
+
+ <!-- Enable suppression comments -->
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CHECKSTYLE IGNORE\s+(\S+)"/>
+ <property name="onCommentFormat" value="CHECKSTYLE END IGNORE\s+(\S+)"/>
+ <property name="checkFormat" value="$1"/>
+ </module>
+ <module name="SuppressWithNearbyCommentFilter">
+ <!-- Syntax is "SUPPRESS CHECKSTYLE name" -->
+ <property name="commentFormat" value="SUPPRESS CHECKSTYLE (\w+)"/>
+ <property name="checkFormat" value="$1"/>
+ <property name="influenceFormat" value="1"/>
+ </module>
+</module>
diff --git a/etc/MANIFEST b/etc/MANIFEST
deleted file mode 100644
index 7401ff4..0000000
--- a/etc/MANIFEST
+++ /dev/null
@@ -1,2 +0,0 @@
-Class-Path: gnu.getopt.jar
-Main-Class: org.anarres.cpp.Main
diff --git a/etc/build.properties b/etc/build.properties
deleted file mode 100644
index a07cd9c..0000000
--- a/etc/build.properties
+++ /dev/null
@@ -1,64 +0,0 @@
-global.version = 1.2.8
-global.name = anarres-cpp
-
-global.dir.arch = ${global.dir.root}/arch
-global.dir.etc = ${global.dir.root}/etc
-global.dir.lib = ${global.dir.root}/lib
-global.dir.tmp = ${global.dir.root}/build/tmp
-
-global.dir.licenses = ${global.dir.etc}/licenses
-
-global.dir.src = ${global.dir.root}/src
-global.dir.src.docs = ${global.dir.root}/src/docs
-global.dir.src.java = ${global.dir.root}/src/java
-global.dir.src.resources = ${global.dir.root}/src/resources
-global.dir.src.scripts = ${global.dir.root}/src/scripts
-global.dir.src.sql = ${global.dir.root}/src/sql
-global.dir.src.tests = ${global.dir.root}/src/tests
-global.dir.src.tools = ${global.dir.root}/src/tools
-
-# XXX Add hacks.
-
-global.dir.build = ${global.dir.root}/build
-global.dir.build.classes = ${global.dir.root}/build/classes
-global.dir.build.cobertura = ${global.dir.root}/build/cobertura
-global.dir.build.depcache = ${global.dir.root}/build/depcache
-global.dir.build.dist = ${global.dir.root}/build/dist
-global.dir.build.docs = ${global.dir.root}/build/docs
-global.dir.build.hbm = ${global.dir.root}/build/hbm
-global.dir.build.jar = ${global.dir.root}/build/jar
-global.dir.build.java = ${global.dir.root}/build/java
-global.dir.build.javadoc = ${global.dir.root}/build/javadoc
-global.dir.build.lib = ${global.dir.root}/build/lib
-global.dir.build.reports = ${global.dir.root}/build/reports
-global.dir.build.sql = ${global.dir.root}/build/sql
-global.dir.build.tar = ${global.dir.root}/build/tar
-global.dir.build.test-xml = ${global.dir.root}/build/test-xml
-global.dir.build.tests = ${global.dir.root}/build/tests
-global.dir.build.tools = ${global.dir.root}/build/tools
-global.dir.build.wsdd = ${global.dir.root}/build/wsdd
-global.dir.build.wsdl = ${global.dir.root}/build/wsdl
-global.dir.build.xml = ${global.dir.root}/build/xml
-
-global.file.jar.name = ${global.name}.jar
-global.file.jar = ${global.dir.build.tar}/lib/${global.file.jar.name}
-global.file.bintar.name = ${global.name}-bin-${global.version}.tar.gz
-global.file.bintar = ${global.dir.build.dist}/${global.file.bintar.name}
-global.file.srctar.name = ${global.name}-src-${global.version}.tar.gz
-global.file.srctar = ${global.dir.build.dist}/${global.file.srctar.name}
-
-build.compiler = modern
-system.javac.optimize = true
-system.javac.debug = true
-system.javac.source = 1.5
-system.javac.target = ${system.javac.source}
-system.javac.deprecation = true
-
-# system.javadoc.access = package
-system.javadoc.access = protected
-system.javadoc.source = ${system.javac.source}
-system.javadoc.offline = false
-
-system.jar.compress = true
-
-wsdl.server.http.url = http://localhost:8080/axis
diff --git a/etc/checkstyle/config.xml b/etc/checkstyle/config.xml
deleted file mode 100644
index 07ff52a..0000000
--- a/etc/checkstyle/config.xml
+++ /dev/null
@@ -1,178 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE module PUBLIC
- "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
- "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
-
-<!--
-
- Checkstyle configuration that checks the sun coding conventions from:
-
- - the Java Language Specification at
- http://java.sun.com/docs/books/jls/second_edition/html/index.html
-
- - the Sun Code Conventions at http://java.sun.com/docs/codeconv/
-
- - the Javadoc guidelines at
- http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
-
- - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
-
- - some best practices
-
- Checkstyle is very configurable. Be sure to read the documentation at
- http://checkstyle.sf.net (or in your downloaded distribution).
-
- Most Checks are configurable, be sure to consult the documentation.
-
- To completely disable a check, just comment it out or delete it from the file.
-
- Finally, it is worth reading the documentation.
-
--->
-<!--
- Modified for karmaphere use
--->
-
-<module name="Checker">
-
- <!-- Checks that a package.html file exists for each package. -->
- <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
- <!-- Currently too numerous, disabled -->
- <!--module name="PackageHtml"/-->
-
- <!-- Checks whether files end with a new line. -->
- <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
- <module name="NewlineAtEndOfFile"/>
-
- <!-- Checks that property files contain the same keys. -->
- <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
- <module name="Translation"/>
-
-
- <module name="TreeWalker">
-
- <!-- Checks for Javadoc comments. -->
- <!-- See http://checkstyle.sf.net/config_javadoc.html -->
- <!-- Currently too numerous -->
- <!--module name="JavadocMethod"/-->
- <!--module name="JavadocType"/-->
- <!--module name="JavadocVariable"/-->
- <!--module name="JavadocStyle"/-->
-
-
- <!-- Checks for Naming Conventions. -->
- <!-- See http://checkstyle.sf.net/config_naming.html -->
- <module name="ConstantName"/>
- <module name="LocalFinalVariableName"/>
- <module name="LocalVariableName"/>
- <module name="MemberName"/>
- <module name="MethodName"/>
- <module name="PackageName"/>
- <module name="ParameterName"/>
- <module name="StaticVariableName"/>
- <module name="TypeName"/>
-
-
- <!-- Checks for Headers -->
- <!-- See http://checkstyle.sf.net/config_header.html -->
- <!-- <module name="Header"> -->
- <!-- The follow property value demonstrates the ability -->
- <!-- to have access to ANT properties. In this case it uses -->
- <!-- the ${basedir} property to allow Checkstyle to be run -->
- <!-- from any directory within a project. See property -->
- <!-- expansion, -->
- <!-- http://checkstyle.sf.net/config.html#properties -->
- <!-- <property -->
- <!-- name="headerFile" -->
- <!-- value="${basedir}/java.header"/> -->
- <!-- </module> -->
-
- <!-- Following interprets the header file as regular expressions. -->
- <!-- <module name="RegexpHeader"/> -->
-
-
- <!-- Checks for imports -->
- <!-- See http://checkstyle.sf.net/config_import.html -->
- <module name="AvoidStarImport"/>
- <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
- <module name="RedundantImport"/>
- <module name="UnusedImports"/>
-
-
- <!-- Checks for Size Violations. -->
- <!-- See http://checkstyle.sf.net/config_sizes.html -->
- <module name="FileLength"/>
- <module name="LineLength"/>
- <module name="MethodLength"/>
- <module name="ParameterNumber"/>
-
-
- <!-- Checks for whitespace -->
- <!-- See http://checkstyle.sf.net/config_whitespace.html -->
- <module name="EmptyForIteratorPad"/>
- <module name="MethodParamPad"/>
- <module name="NoWhitespaceAfter"/>
- <module name="NoWhitespaceBefore"/>
- <module name="OperatorWrap"/>
- <module name="ParenPad"/>
- <module name="TypecastParenPad"/>
- <!--
- <module name="TabCharacter"/>
- <module name="WhitespaceAfter"/>
- <module name="WhitespaceAround"/>
- -->
-
-
- <!-- Modifier Checks -->
- <!-- See http://checkstyle.sf.net/config_modifiers.html -->
- <module name="ModifierOrder"/>
- <module name="RedundantModifier"/>
-
-
- <!-- Checks for blocks. You know, those {}'s -->
- <!-- See http://checkstyle.sf.net/config_blocks.html -->
- <module name="AvoidNestedBlocks"/>
- <module name="EmptyBlock"/>
- <module name="LeftCurly"/>
- <!--module name="NeedBraces"/-->
- <module name="RightCurly"/>
-
-
- <!-- Checks for common coding problems -->
- <!-- See http://checkstyle.sf.net/config_coding.html -->
- <module name="AvoidInlineConditionals"/>
- <module name="DoubleCheckedLocking"/> <!-- MY FAVOURITE -->
- <module name="EmptyStatement"/>
- <module name="EqualsHashCode"/>
- <module name="HiddenField"/>
- <module name="IllegalInstantiation"/>
- <module name="InnerAssignment"/>
- <module name="MagicNumber"/>
- <module name="MissingSwitchDefault"/>
- <module name="RedundantThrows"/>
- <module name="SimplifyBooleanExpression"/>
- <module name="SimplifyBooleanReturn"/>
-
- <!-- Checks for class design -->
- <!-- See http://checkstyle.sf.net/config_design.html -->
- <module name="DesignForExtension"/>
- <module name="FinalClass"/>
- <module name="HideUtilityClassConstructor"/>
- <module name="InterfaceIsType"/>
- <module name="VisibilityModifier"/>
-
-
- <!-- Miscellaneous other checks. -->
- <!-- See http://checkstyle.sf.net/config_misc.html -->
- <module name="ArrayTypeStyle"/>
- <module name="FinalParameters"/>
- <module name="GenericIllegalRegexp">
- <property name="format" value="\s+$"/>
- <property name="message" value="Line has trailing spaces."/>
- </module>
- <module name="TodoComment"/>
- <module name="UpperEll"/>
-
- </module>
-
-</module>
diff --git a/etc/global.xml b/etc/global.xml
deleted file mode 100644
index d465f07..0000000
--- a/etc/global.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global" default="all" basedir=".">
-
- <dirname property="global.dir.etc" file="${ant.file}"/>
- <dirname property="global.dir.root" file="${global.dir.etc}"/>
- <property file="${global.dir.etc}/custom.properties" />
- <property file="${global.dir.etc}/build.properties" />
- <property file="${global.dir.etc}/log4j.properties" />
-
- <path id="compile-classpath">
- <fileset dir="${global.dir.lib}">
- <include name="runtime/**/*.jar"/>
- </fileset>
- <fileset dir="/usr/share/ant/lib">
- <include name="*.jar" />
- </fileset>
- </path>
-
- <path id="runtime-classpath">
- <pathelement path="${global.dir.build.classes}" />
- <path refid="compile-classpath" />
- </path>
-
- <path id="antcontrib-classpath">
- <fileset dir="${global.dir.lib}/ant">
- <include name="*.jar"/>
- </fileset>
- </path>
-
- <path id="checkstyle-classpath">
- <fileset dir="${global.dir.lib}/checkstyle">
- <include name="**/*.jar"/>
- </fileset>
- </path>
-
- <path id="vpp-classpath">
- <fileset dir="${global.dir.lib}/vpp">
- <include name="**/*.jar" />
- </fileset>
- </path>
-
- <path id="findbugs-classpath">
- <fileset dir="${global.dir.lib}/findbugs">
- <include name="**/*.jar" />
- </fileset>
- </path>
-
- <path id="jrat-classpath">
- <fileset dir="${global.dir.lib}/jrat">
- <include name="*.jar"/>
- </fileset>
- </path>
-
- <path id="cobertura-classpath">
- <fileset dir="${global.dir.lib}/log4j">
- <include name="log4j-*.jar"/>
- </fileset>
- <fileset dir="${global.dir.lib}/cobertura">
- <include name="**/*.jar" />
- </fileset>
- </path>
-
- <path id="svn-classpath">
- <fileset dir="${global.dir.lib}/svn">
- <include name="**/*.jar" />
- </fileset>
- </path>
-
- <!-- classpath for compiling junit tasks -->
- <path id="junit-compile-classpath">
- <fileset dir="${global.dir.lib}/junit">
- <include name="**/*.jar" />
- </fileset>
- <path refid="runtime-classpath" />
- </path>
-
- <!-- classpath for running junit tasks -->
- <path id="junit-run-classpath">
- <pathelement path="${global.dir.build.tests}"/>
- <pathelement path="${global.dir.build.cobertura}"/>
- <pathelement path="${global.dir.src.resources}" />
- <pathelement path="${global.dir.src.tests}" />
- <path refid="junit-compile-classpath"/>
- <path refid="cobertura-classpath"/>
- <path refid="jrat-classpath"/>
- </path>
-
-
-
-
-
-
- <import file="${global.dir.etc}/targets/global-checkstyle.xml" />
- <import file="${global.dir.etc}/targets/global-clean.xml" />
- <import file="${global.dir.etc}/targets/global-cobertura.xml" />
- <import file="${global.dir.etc}/targets/global-compile.xml" />
- <import file="${global.dir.etc}/targets/global-findbugs.xml" />
- <import file="${global.dir.etc}/targets/global-inject.xml" />
- <import file="${global.dir.etc}/targets/global-jar.xml" />
- <import file="${global.dir.etc}/targets/global-javadoc.xml" />
- <import file="${global.dir.etc}/targets/global-junit.xml" />
- <import file="${global.dir.etc}/targets/global-tar.xml" />
- <import file="${global.dir.etc}/targets/global-taskdefs.xml" />
- <import file="${global.dir.etc}/targets/global-verify.xml" />
- <import file="${global.dir.etc}/targets/global-vpp.xml" />
-
-</project>
diff --git a/etc/javadoc/.gitignore b/etc/javadoc/.gitignore
deleted file mode 100644
index e69de29..0000000
--- a/etc/javadoc/.gitignore
+++ /dev/null
diff --git a/etc/junit/xsl/junit-frames.xsl b/etc/junit/xsl/junit-frames.xsl
deleted file mode 100644
index a9d37a5..0000000
--- a/etc/junit/xsl/junit-frames.xsl
+++ /dev/null
@@ -1,716 +0,0 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
- xmlns:lxslt="http://xml.apache.org/xslt"
- xmlns:redirect="http://xml.apache.org/xalan/redirect"
- xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
- extension-element-prefixes="redirect">
-<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
-<xsl:decimal-format decimal-separator="." grouping-separator=","/>
-<!--
- Copyright 2001-2004 The Apache Software Foundation
-
- 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.
- -->
-
-<!--
-
- Sample stylesheet to be used with Ant JUnitReport output.
-
- It creates a set of HTML files a la javadoc where you can browse easily
- through all packages and classes.
-
- @author Stephane Bailliez <a href="mailto:[email protected]"/>
- @author Erik Hatcher <a href="mailto:[email protected]"/>
- @author Martijn Kruithof <a href="mailto:[email protected]"/>
-
--->
-<xsl:param name="output.dir" select="'.'"/>
-
-
-<xsl:template match="testsuites">
- <!-- create the index.html -->
- <redirect:write file="{$output.dir}/index.html">
- <xsl:call-template name="index.html"/>
- </redirect:write>
-
- <!-- create the stylesheet.css -->
- <redirect:write file="{$output.dir}/stylesheet.css">
- <xsl:call-template name="stylesheet.css"/>
- </redirect:write>
-
- <!-- create the overview-packages.html at the root -->
- <redirect:write file="{$output.dir}/overview-summary.html">
- <xsl:apply-templates select="." mode="overview.packages"/>
- </redirect:write>
-
- <!-- create the all-packages.html at the root -->
- <redirect:write file="{$output.dir}/overview-frame.html">
- <xsl:apply-templates select="." mode="all.packages"/>
- </redirect:write>
-
- <!-- create the all-classes.html at the root -->
- <redirect:write file="{$output.dir}/allclasses-frame.html">
- <xsl:apply-templates select="." mode="all.classes"/>
- </redirect:write>
-
- <!-- process all packages -->
- <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
- <xsl:call-template name="package">
- <xsl:with-param name="name" select="@package"/>
- </xsl:call-template>
- </xsl:for-each>
-</xsl:template>
-
-
-<xsl:template name="package">
- <xsl:param name="name"/>
- <xsl:variable name="package.dir">
- <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
- <xsl:if test="$name = ''">.</xsl:if>
- </xsl:variable>
- <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
- <!-- create a classes-list.html in the package directory -->
- <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
- <xsl:call-template name="classes.list">
- <xsl:with-param name="name" select="$name"/>
- </xsl:call-template>
- </redirect:write>
-
- <!-- create a package-summary.html in the package directory -->
- <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
- <xsl:call-template name="package.summary">
- <xsl:with-param name="name" select="$name"/>
- </xsl:call-template>
- </redirect:write>
-
- <!-- for each class, creates a @name.html -->
- <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
- <xsl:for-each select="/testsuites/testsuite[@package = $name]">
- <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
- <xsl:apply-templates select="." mode="class.details"/>
- </redirect:write>
- <xsl:if test="string-length(./system-out)!=0">
- <redirect:write file="{$output.dir}/{$package.dir}/{@name}-out.txt">
- <xsl:value-of select="./system-out" />
- </redirect:write>
- </xsl:if>
- <xsl:if test="string-length(./system-err)!=0">
- <redirect:write file="{$output.dir}/{$package.dir}/{@name}-err.txt">
- <xsl:value-of select="./system-err" />
- </redirect:write>
- </xsl:if>
- </xsl:for-each>
-</xsl:template>
-
-<xsl:template name="index.html">
-<html>
- <head>
- <title>Unit Test Results.</title>
- </head>
- <frameset cols="20%,80%">
- <frameset rows="30%,70%">
- <frame src="overview-frame.html" name="packageListFrame"/>
- <frame src="allclasses-frame.html" name="classListFrame"/>
- </frameset>
- <frame src="overview-summary.html" name="classFrame"/>
- <noframes>
- <h2>Frame Alert</h2>
- <p>
- This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
- </p>
- </noframes>
- </frameset>
-</html>
-</xsl:template>
-
-<!-- this is the stylesheet css to use for nearly everything -->
-<xsl:template name="stylesheet.css">
-body {
- font:normal 68% verdana,arial,helvetica;
- color:#000000;
-}
-table tr td, table tr th {
- font-size: 68%;
-}
-table.details tr th{
- font-weight: bold;
- text-align:left;
- background:#a6caf0;
-}
-table.details tr td{
- background:#eeeee0;
-}
-
-p {
- line-height:1.5em;
- margin-top:0.5em; margin-bottom:1.0em;
-}
-h1 {
- margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
-}
-h2 {
- margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
-}
-h3 {
- margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
-}
-h4 {
- margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
-}
-h5 {
- margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
-}
-h6 {
- margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
-}
-.Error {
- font-weight:bold; color:red;
-}
-.Failure {
- font-weight:bold; color:purple;
-}
-.Properties {
- text-align:right;
-}
-</xsl:template>
-
-
-<!-- ======================================================================
- This page is created for every testsuite class.
- It prints a summary of the testsuite and detailed information about
- testcase methods.
- ====================================================================== -->
-<xsl:template match="testsuite" mode="class.details">
- <xsl:variable name="package.name" select="@package"/>
- <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
- <html>
- <head>
- <title>Unit Test Results: <xsl:value-of select="$class.name"/></title>
- <xsl:call-template name="create.stylesheet.link">
- <xsl:with-param name="package.name" select="$package.name"/>
- </xsl:call-template>
- <script type="text/javascript" language="JavaScript">
- var TestCases = new Array();
- var cur;
- <xsl:apply-templates select="properties"/>
- </script>
- <script type="text/javascript" language="JavaScript"><![CDATA[
- function displayProperties (name) {
- var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
- var doc = win.document.open();
- doc.write("<html><head><title>Properties of " + name + "</title>");
- doc.write("<style type=\"text/css\">");
- doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
- doc.write("table tr td, table tr th { font-size: 68%; }");
- doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
- doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
- doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
- doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
- doc.write("</style>");
- doc.write("</head><body>");
- doc.write("<h3>Properties of " + name + "</h3>");
- doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
- doc.write("<table class='properties'>");
- doc.write("<tr><th>Name</th><th>Value</th></tr>");
- for (prop in TestCases[name]) {
- doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
- }
- doc.write("</table>");
- doc.write("</body></html>");
- doc.close();
- win.focus();
- }
- ]]>
- </script>
- </head>
- <body>
- <xsl:call-template name="pageHeader"/>
- <h3>Class <xsl:value-of select="$class.name"/></h3>
-
-
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testsuite.test.header"/>
- <xsl:apply-templates select="." mode="print.test"/>
- </table>
-
- <h2>Tests</h2>
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testcase.test.header"/>
- <!--
- test can even not be started at all (failure to load the class)
- so report the error directly
- -->
- <xsl:if test="./error">
- <tr class="Error">
- <td colspan="4"><xsl:apply-templates select="./error"/></td>
- </tr>
- </xsl:if>
- <xsl:apply-templates select="./testcase" mode="print.test"/>
- </table>
- <div class="Properties">
- <a>
- <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
- Properties &#187;
- </a>
- </div>
- <xsl:if test="string-length(./system-out)!=0">
- <div class="Properties">
- <a>
- <xsl:attribute name="href">./<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
- System.out &#187;
- </a>
- </div>
- </xsl:if>
- <xsl:if test="string-length(./system-err)!=0">
- <div class="Properties">
- <a>
- <xsl:attribute name="href">./<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
- System.err &#187;
- </a>
- </div>
- </xsl:if>
- </body>
- </html>
-</xsl:template>
-
- <!--
- Write properties into a JavaScript data structure.
- This is based on the original idea by Erik Hatcher ([email protected])
- -->
- <xsl:template match="properties">
- cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
- <xsl:for-each select="property">
- <xsl:sort select="@name"/>
- cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
- </xsl:for-each>
- </xsl:template>
-
-
-<!-- ======================================================================
- This page is created for every package.
- It prints the name of all classes that belongs to this package.
- @param name the package name to print classes.
- ====================================================================== -->
-<!-- list of classes in a package -->
-<xsl:template name="classes.list">
- <xsl:param name="name"/>
- <html>
- <head>
- <title>Unit Test Classes: <xsl:value-of select="$name"/></title>
- <xsl:call-template name="create.stylesheet.link">
- <xsl:with-param name="package.name" select="$name"/>
- </xsl:call-template>
- </head>
- <body>
- <table width="100%">
- <tr>
- <td nowrap="nowrap">
- <h2><a href="package-summary.html" target="classFrame">
- <xsl:value-of select="$name"/>
- <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
- </a></h2>
- </td>
- </tr>
- </table>
-
- <h2>Classes</h2>
- <table width="100%">
- <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
- <xsl:sort select="@name"/>
- <tr>
- <td nowrap="nowrap">
- <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
- </td>
- </tr>
- </xsl:for-each>
- </table>
- </body>
- </html>
-</xsl:template>
-
-
-<!--
- Creates an all-classes.html file that contains a link to all package-summary.html
- on each class.
--->
-<xsl:template match="testsuites" mode="all.classes">
- <html>
- <head>
- <title>All Unit Test Classes</title>
- <xsl:call-template name="create.stylesheet.link">
- <xsl:with-param name="package.name"/>
- </xsl:call-template>
- </head>
- <body>
- <h2>Classes</h2>
- <table width="100%">
- <xsl:apply-templates select="testsuite" mode="all.classes">
- <xsl:sort select="@name"/>
- </xsl:apply-templates>
- </table>
- </body>
- </html>
-</xsl:template>
-
-<xsl:template match="testsuite" mode="all.classes">
- <xsl:variable name="package.name" select="@package"/>
- <tr>
- <td nowrap="nowrap">
- <a target="classFrame">
- <xsl:attribute name="href">
- <xsl:if test="not($package.name='')">
- <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
- </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
- </xsl:attribute>
- <xsl:value-of select="@name"/>
- </a>
- </td>
- </tr>
-</xsl:template>
-
-
-<!--
- Creates an html file that contains a link to all package-summary.html files on
- each package existing on testsuites.
- @bug there will be a problem here, I don't know yet how to handle unnamed package :(
--->
-<xsl:template match="testsuites" mode="all.packages">
- <html>
- <head>
- <title>All Unit Test Packages</title>
- <xsl:call-template name="create.stylesheet.link">
- <xsl:with-param name="package.name"/>
- </xsl:call-template>
- </head>
- <body>
- <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
- <h2>Packages</h2>
- <table width="100%">
- <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
- <xsl:sort select="@package"/>
- </xsl:apply-templates>
- </table>
- </body>
- </html>
-</xsl:template>
-
-<xsl:template match="testsuite" mode="all.packages">
- <tr>
- <td nowrap="nowrap">
- <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
- <xsl:value-of select="@package"/>
- <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
- </a>
- </td>
- </tr>
-</xsl:template>
-
-
-<xsl:template match="testsuites" mode="overview.packages">
- <html>
- <head>
- <title>Unit Test Results: Summary</title>
- <xsl:call-template name="create.stylesheet.link">
- <xsl:with-param name="package.name"/>
- </xsl:call-template>
- </head>
- <body>
- <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
- <xsl:call-template name="pageHeader"/>
- <h2>Summary</h2>
- <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
- <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
- <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
- <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
- <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <tr valign="top">
- <th>Tests</th>
- <th>Failures</th>
- <th>Errors</th>
- <th>Success rate</th>
- <th>Time</th>
- </tr>
- <tr valign="top">
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
- <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
- <xsl:otherwise>Pass</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <td><xsl:value-of select="$testCount"/></td>
- <td><xsl:value-of select="$failureCount"/></td>
- <td><xsl:value-of select="$errorCount"/></td>
- <td>
- <xsl:call-template name="display-percent">
- <xsl:with-param name="value" select="$successRate"/>
- </xsl:call-template>
- </td>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="$timeCount"/>
- </xsl:call-template>
- </td>
-
- </tr>
- </table>
- <table border="0" width="95%">
- <tr>
- <td style="text-align: justify;">
- Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
- </td>
- </tr>
- </table>
-
- <h2>Packages</h2>
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testsuite.test.header"/>
- <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
- <xsl:sort select="@package" order="ascending"/>
- <!-- get the node set containing all testsuites that have the same package -->
- <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
- <tr valign="top">
- <!-- display a failure if there is any failure/error in the package -->
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
- <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
- <xsl:otherwise>Pass</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <td><a href="./{translate(@package,'.','/')}/package-summary.html">
- <xsl:value-of select="@package"/>
- <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
- </a></td>
- <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
- <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
- <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
- </xsl:call-template>
- </td>
- </tr>
- </xsl:for-each>
- </table>
- </body>
- </html>
-</xsl:template>
-
-
-<xsl:template name="package.summary">
- <xsl:param name="name"/>
- <html>
- <head>
- <xsl:call-template name="create.stylesheet.link">
- <xsl:with-param name="package.name" select="$name"/>
- </xsl:call-template>
- </head>
- <body>
- <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
- <xsl:call-template name="pageHeader"/>
- <h3>Package <xsl:value-of select="$name"/></h3>
-
- <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="class.metrics.header"/>
- <xsl:apply-templates select="." mode="print.metrics"/>
- </table-->
-
- <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
- <xsl:if test="count($insamepackage) &gt; 0">
- <h2>Classes</h2>
- <p>
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testsuite.test.header"/>
- <xsl:apply-templates select="$insamepackage" mode="print.test">
- <xsl:sort select="@name"/>
- </xsl:apply-templates>
- </table>
- </p>
- </xsl:if>
- </body>
- </html>
-</xsl:template>
-
-
-<!--
- transform string like a.b.c to ../../../
- @param path the path to transform into a descending directory path
--->
-<xsl:template name="path">
- <xsl:param name="path"/>
- <xsl:if test="contains($path,'.')">
- <xsl:text>../</xsl:text>
- <xsl:call-template name="path">
- <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:if test="not(contains($path,'.')) and not($path = '')">
- <xsl:text>../</xsl:text>
- </xsl:if>
-</xsl:template>
-
-
-<!-- create the link to the stylesheet based on the package name -->
-<xsl:template name="create.stylesheet.link">
- <xsl:param name="package.name"/>
- <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
-</xsl:template>
-
-
-<!-- Page HEADER -->
-<xsl:template name="pageHeader">
- <h1>Unit Test Results</h1>
- <table width="100%">
- <tr>
- <td align="left"></td>
- <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://jakarta.apache.org/">Ant</a>.</td>
- </tr>
- </table>
- <hr size="1"/>
-</xsl:template>
-
-<!-- class header -->
-<xsl:template name="testsuite.test.header">
- <tr valign="top">
- <th width="80%">Name</th>
- <th>Tests</th>
- <th>Errors</th>
- <th>Failures</th>
- <th nowrap="nowrap">Time(s)</th>
- </tr>
-</xsl:template>
-
-<!-- method header -->
-<xsl:template name="testcase.test.header">
- <tr valign="top">
- <th>Name</th>
- <th>Status</th>
- <th width="80%">Type</th>
- <th nowrap="nowrap">Time(s)</th>
- </tr>
-</xsl:template>
-
-
-<!-- class information -->
-<xsl:template match="testsuite" mode="print.test">
- <tr valign="top">
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
- <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
- <xsl:otherwise>Pass</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
- <td><xsl:apply-templates select="@tests"/></td>
- <td><xsl:apply-templates select="@errors"/></td>
- <td><xsl:apply-templates select="@failures"/></td>
- <td><xsl:call-template name="display-time">
- <xsl:with-param name="value" select="@time"/>
- </xsl:call-template>
- </td>
- </tr>
-</xsl:template>
-
-<xsl:template match="testcase" mode="print.test">
- <tr valign="top">
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="error">Error</xsl:when>
- <xsl:when test="failure">Failure</xsl:when>
- <xsl:otherwise>TableRowColor</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <td><xsl:value-of select="concat( @classname, '.', @name)"/></td>
- <xsl:choose>
- <xsl:when test="failure">
- <td>Failure</td>
- <td><xsl:apply-templates select="failure"/></td>
- </xsl:when>
- <xsl:when test="error">
- <td>Error</td>
- <td><xsl:apply-templates select="error"/></td>
- </xsl:when>
- <xsl:otherwise>
- <td>Success</td>
- <td></td>
- </xsl:otherwise>
- </xsl:choose>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="@time"/>
- </xsl:call-template>
- </td>
- </tr>
-</xsl:template>
-
-
-<!-- Note : the below template error and failure are the same style
- so just call the same style store in the toolkit template -->
-<xsl:template match="failure">
- <xsl:call-template name="display-failures"/>
-</xsl:template>
-
-<xsl:template match="error">
- <xsl:call-template name="display-failures"/>
-</xsl:template>
-
-<!-- Style for the error and failure in the testcase template -->
-<xsl:template name="display-failures">
- <xsl:choose>
- <xsl:when test="not(@message)">N/A</xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="@message"/>
- </xsl:otherwise>
- </xsl:choose>
- <!-- display the stacktrace -->
- <br/><br/>
- <code>
- <xsl:call-template name="br-replace">
- <xsl:with-param name="word" select="."/>
- </xsl:call-template>
- </code>
- <!-- the latter is better but might be problematic for non-21" monitors... -->
- <!--pre><xsl:value-of select="."/></pre-->
-</xsl:template>
-
-<xsl:template name="JS-escape">
- <xsl:param name="string"/>
- <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
- <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
- <xsl:value-of select="$tmp2"/>
-</xsl:template>
-
-
-<!--
- template that will convert a carriage return into a br tag
- @param word the text from which to convert CR to BR tag
--->
-<xsl:template name="br-replace">
- <xsl:param name="word"/>
- <xsl:param name="br"><br/></xsl:param>
- <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
-</xsl:template>
-
-<xsl:template name="display-time">
- <xsl:param name="value"/>
- <xsl:value-of select="format-number($value,'0.000')"/>
-</xsl:template>
-
-<xsl:template name="display-percent">
- <xsl:param name="value"/>
- <xsl:value-of select="format-number($value,'0.00%')"/>
-</xsl:template>
-</xsl:stylesheet>
-
diff --git a/etc/junit/xsl/junit-noframes.xsl b/etc/junit/xsl/junit-noframes.xsl
deleted file mode 100644
index 25487f8..0000000
--- a/etc/junit/xsl/junit-noframes.xsl
+++ /dev/null
@@ -1,461 +0,0 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
- xmlns:lxslt="http://xml.apache.org/xslt"
- xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils">
-<xsl:output method="html" indent="yes" encoding="US-ASCII"
- doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
-<xsl:decimal-format decimal-separator="." grouping-separator="," />
-<!--
- Copyright 2001-2004 The Apache Software Foundation
-
- 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.
- -->
-
-<!--
-
- Sample stylesheet to be used with Ant JUnitReport output.
-
- It creates a non-framed report that can be useful to send via
- e-mail or such.
-
- @author Stephane Bailliez <a href="mailto:[email protected]"/>
- @author Erik Hatcher <a href="mailto:[email protected]"/>
-
--->
-<xsl:template match="testsuites">
- <html>
- <head>
- <title>Unit Test Results</title>
- <style type="text/css">
- body {
- font:normal 68% verdana,arial,helvetica;
- color:#000000;
- }
- table tr td, table tr th {
- font-size: 68%;
- }
- table.details tr th{
- font-weight: bold;
- text-align:left;
- background:#a6caf0;
- }
- table.details tr td{
- background:#eeeee0;
- }
-
- p {
- line-height:1.5em;
- margin-top:0.5em; margin-bottom:1.0em;
- }
- h1 {
- margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
- }
- h2 {
- margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
- }
- h3 {
- margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
- }
- h4 {
- margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
- }
- h5 {
- margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
- }
- h6 {
- margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
- }
- .Error {
- font-weight:bold; color:red;
- }
- .Failure {
- font-weight:bold; color:purple;
- }
- .Properties {
- text-align:right;
- }
- </style>
- <script type="text/javascript" language="JavaScript">
- var TestCases = new Array();
- var cur;
- <xsl:for-each select="./testsuite">
- <xsl:apply-templates select="properties"/>
- </xsl:for-each>
-
- </script>
- <script type="text/javascript" language="JavaScript"><![CDATA[
- function displayProperties (name) {
- var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
- var doc = win.document.open();
- doc.write("<html><head><title>Properties of " + name + "</title>");
- doc.write("<style>")
- doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
- doc.write("table tr td, table tr th { font-size: 68%; }");
- doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
- doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
- doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
- doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
- doc.write("</style>");
- doc.write("</head><body>");
- doc.write("<h3>Properties of " + name + "</h3>");
- doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
- doc.write("<table class='properties'>");
- doc.write("<tr><th>Name</th><th>Value</th></tr>");
- for (prop in TestCases[name]) {
- doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
- }
- doc.write("</table>");
- doc.write("</body></html>");
- doc.close();
- win.focus();
- }
- ]]>
- </script>
- </head>
- <body>
- <a name="top"></a>
- <xsl:call-template name="pageHeader"/>
-
- <!-- Summary part -->
- <xsl:call-template name="summary"/>
- <hr size="1" width="95%" align="left"/>
-
- <!-- Package List part -->
- <xsl:call-template name="packagelist"/>
- <hr size="1" width="95%" align="left"/>
-
- <!-- For each package create its part -->
- <xsl:call-template name="packages"/>
- <hr size="1" width="95%" align="left"/>
-
- <!-- For each class create the part -->
- <xsl:call-template name="classes"/>
-
- </body>
- </html>
-</xsl:template>
-
-
-
- <!-- ================================================================== -->
- <!-- Write a list of all packages with an hyperlink to the anchor of -->
- <!-- of the package name. -->
- <!-- ================================================================== -->
- <xsl:template name="packagelist">
- <h2>Packages</h2>
- Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers.
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testsuite.test.header"/>
- <!-- list all packages recursively -->
- <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
- <xsl:sort select="@package"/>
- <xsl:variable name="testsuites-in-package" select="/testsuites/testsuite[./@package = current()/@package]"/>
- <xsl:variable name="testCount" select="sum($testsuites-in-package/@tests)"/>
- <xsl:variable name="errorCount" select="sum($testsuites-in-package/@errors)"/>
- <xsl:variable name="failureCount" select="sum($testsuites-in-package/@failures)"/>
- <xsl:variable name="timeCount" select="sum($testsuites-in-package/@time)"/>
-
- <!-- write a summary for the package -->
- <tr valign="top">
- <!-- set a nice color depending if there is an error/failure -->
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
- <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- <td><a href="#{@package}"><xsl:value-of select="@package"/></a></td>
- <td><xsl:value-of select="$testCount"/></td>
- <td><xsl:value-of select="$errorCount"/></td>
- <td><xsl:value-of select="$failureCount"/></td>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="$timeCount"/>
- </xsl:call-template>
- </td>
- </tr>
- </xsl:for-each>
- </table>
- </xsl:template>
-
-
- <!-- ================================================================== -->
- <!-- Write a package level report -->
- <!-- It creates a table with values from the document: -->
- <!-- Name | Tests | Errors | Failures | Time -->
- <!-- ================================================================== -->
- <xsl:template name="packages">
- <!-- create an anchor to this package name -->
- <xsl:for-each select="/testsuites/testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
- <xsl:sort select="@package"/>
- <a name="{@package}"></a>
- <h3>Package <xsl:value-of select="@package"/></h3>
-
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testsuite.test.header"/>
-
- <!-- match the testsuites of this package -->
- <xsl:apply-templates select="/testsuites/testsuite[./@package = current()/@package]" mode="print.test"/>
- </table>
- <a href="#top">Back to top</a>
- <p/>
- <p/>
- </xsl:for-each>
- </xsl:template>
-
- <xsl:template name="classes">
- <xsl:for-each select="testsuite">
- <xsl:sort select="@name"/>
- <!-- create an anchor to this class name -->
- <a name="{@name}"></a>
- <h3>TestCase <xsl:value-of select="@name"/></h3>
-
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <xsl:call-template name="testcase.test.header"/>
- <!--
- test can even not be started at all (failure to load the class)
- so report the error directly
- -->
- <xsl:if test="./error">
- <tr class="Error">
- <td colspan="4"><xsl:apply-templates select="./error"/></td>
- </tr>
- </xsl:if>
- <xsl:apply-templates select="./testcase" mode="print.test"/>
- </table>
- <div class="Properties">
- <a>
- <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
- Properties &#187;
- </a>
- </div>
- <p/>
-
- <a href="#top">Back to top</a>
- </xsl:for-each>
- </xsl:template>
-
- <xsl:template name="summary">
- <h2>Summary</h2>
- <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
- <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
- <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
- <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
- <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
- <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
- <tr valign="top">
- <th>Tests</th>
- <th>Failures</th>
- <th>Errors</th>
- <th>Success rate</th>
- <th>Time</th>
- </tr>
- <tr valign="top">
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
- <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- <td><xsl:value-of select="$testCount"/></td>
- <td><xsl:value-of select="$failureCount"/></td>
- <td><xsl:value-of select="$errorCount"/></td>
- <td>
- <xsl:call-template name="display-percent">
- <xsl:with-param name="value" select="$successRate"/>
- </xsl:call-template>
- </td>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="$timeCount"/>
- </xsl:call-template>
- </td>
-
- </tr>
- </table>
- <table border="0" width="95%">
- <tr>
- <td style="text-align: justify;">
- Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
- </td>
- </tr>
- </table>
- </xsl:template>
-
- <!--
- Write properties into a JavaScript data structure.
- This is based on the original idea by Erik Hatcher ([email protected])
- -->
- <xsl:template match="properties">
- cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
- <xsl:for-each select="property">
- <xsl:sort select="@name"/>
- cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
- </xsl:for-each>
- </xsl:template>
-
-<!-- Page HEADER -->
-<xsl:template name="pageHeader">
- <h1>Unit Test Results</h1>
- <table width="100%">
- <tr>
- <td align="left"></td>
- <td align="right">Designed for use with <a href='http://www.junit.org'>JUnit</a> and <a href='http://jakarta.apache.org/ant'>Ant</a>.</td>
- </tr>
- </table>
- <hr size="1"/>
-</xsl:template>
-
-<xsl:template match="testsuite" mode="header">
- <tr valign="top">
- <th width="80%">Name</th>
- <th>Tests</th>
- <th>Errors</th>
- <th>Failures</th>
- <th nowrap="nowrap">Time(s)</th>
- </tr>
-</xsl:template>
-
-<!-- class header -->
-<xsl:template name="testsuite.test.header">
- <tr valign="top">
- <th width="80%">Name</th>
- <th>Tests</th>
- <th>Errors</th>
- <th>Failures</th>
- <th nowrap="nowrap">Time(s)</th>
- </tr>
-</xsl:template>
-
-<!-- method header -->
-<xsl:template name="testcase.test.header">
- <tr valign="top">
- <th>Name</th>
- <th>Status</th>
- <th width="80%">Type</th>
- <th nowrap="nowrap">Time(s)</th>
- </tr>
-</xsl:template>
-
-
-<!-- class information -->
-<xsl:template match="testsuite" mode="print.test">
- <tr valign="top">
- <!-- set a nice color depending if there is an error/failure -->
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
- <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
- </xsl:choose>
- </xsl:attribute>
-
- <!-- print testsuite information -->
- <td><a href="#{@name}"><xsl:value-of select="@name"/></a></td>
- <td><xsl:value-of select="@tests"/></td>
- <td><xsl:value-of select="@errors"/></td>
- <td><xsl:value-of select="@failures"/></td>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="@time"/>
- </xsl:call-template>
- </td>
- </tr>
-</xsl:template>
-
-<xsl:template match="testcase" mode="print.test">
- <tr valign="top">
- <xsl:attribute name="class">
- <xsl:choose>
- <xsl:when test="failure | error">Error</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- <td><xsl:value-of select="@name"/></td>
- <xsl:choose>
- <xsl:when test="failure">
- <td>Failure</td>
- <td><xsl:apply-templates select="failure"/></td>
- </xsl:when>
- <xsl:when test="error">
- <td>Error</td>
- <td><xsl:apply-templates select="error"/></td>
- </xsl:when>
- <xsl:otherwise>
- <td>Success</td>
- <td></td>
- </xsl:otherwise>
- </xsl:choose>
- <td>
- <xsl:call-template name="display-time">
- <xsl:with-param name="value" select="@time"/>
- </xsl:call-template>
- </td>
- </tr>
-</xsl:template>
-
-
-<xsl:template match="failure">
- <xsl:call-template name="display-failures"/>
-</xsl:template>
-
-<xsl:template match="error">
- <xsl:call-template name="display-failures"/>
-</xsl:template>
-
-<!-- Style for the error and failure in the tescase template -->
-<xsl:template name="display-failures">
- <xsl:choose>
- <xsl:when test="not(@message)">N/A</xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="@message"/>
- </xsl:otherwise>
- </xsl:choose>
- <!-- display the stacktrace -->
- <code>
- <br/><br/>
- <xsl:call-template name="br-replace">
- <xsl:with-param name="word" select="."/>
- </xsl:call-template>
- </code>
- <!-- the later is better but might be problematic for non-21" monitors... -->
- <!--pre><xsl:value-of select="."/></pre-->
-</xsl:template>
-
-<xsl:template name="JS-escape">
- <xsl:param name="string"/>
- <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
- <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
- <xsl:value-of select="$tmp2"/>
-</xsl:template>
-
-
-<!--
- template that will convert a carriage return into a br tag
- @param word the text from which to convert CR to BR tag
--->
-<xsl:template name="br-replace">
- <xsl:param name="word"/>
- <xsl:param name="br"><br/></xsl:param>
- <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
-</xsl:template>
-
-<xsl:template name="display-time">
- <xsl:param name="value"/>
- <xsl:value-of select="format-number($value,'0.000')"/>
-</xsl:template>
-
-<xsl:template name="display-percent">
- <xsl:param name="value"/>
- <xsl:value-of select="format-number($value,'0.00%')"/>
-</xsl:template>
-
-</xsl:stylesheet>
-
diff --git a/etc/targets/global-checkstyle.xml b/etc/targets/global-checkstyle.xml
deleted file mode 100644
index 5c15352..0000000
--- a/etc/targets/global-checkstyle.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-checkstyle" basedir=".">
-
- <target name="global-checkstyle" depends="global-vpp"
- unless="test.no.checkstyle">
-
- <mkdir dir="${global.dir.build.reports}" />
-
- <!-- XXX: Stupid checkstyle task doesn't expand it's parameters -->
- <checkstyle config="etc/checkstyle/config.xml"
- failOnViolation="false">
- <classpath refid="checkstyle-classpath" />
- <fileset dir="${global.dir.build.java}" />
- <formatter type="xml"
- toFile="${global.dir.build.reports}/checkstyle.xml" />
- <formatter type="plain"
- toFile="${global.dir.build.reports}/checkstyle.txt" />
- </checkstyle>
-
- </target>
-
-</project>
diff --git a/etc/targets/global-clean.xml b/etc/targets/global-clean.xml
deleted file mode 100644
index 246a2d0..0000000
--- a/etc/targets/global-clean.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-clean" basedir=".">
- <target name="global-clean">
- <delete dir="${global.dir.root}/build" />
- <delete dir="${global.dir.root}/webroot" />
- </target>
-</project>
diff --git a/etc/targets/global-cobertura.xml b/etc/targets/global-cobertura.xml
deleted file mode 100644
index 425a9fd..0000000
--- a/etc/targets/global-cobertura.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-cobertura" basedir=".">
-
- <target name="global-cobertura-instrument" depends="global-taskdefs"
- unless="test.no.cobertura">
- <delete dir="${global.dir.build.cobertura}" />
- <mkdir dir="${global.dir.build.cobertura}" />
-
- <cobertura-instrument
- todir="${global.dir.build.cobertura}"
- datafile="${global.dir.build.cobertura}/cobertura.ser">
- <fileset dir="${global.dir.build.classes}">
- <include name="**/*.class" />
- <exclude name="**/Main.class" />
- <exclude name="**/cmd/**" />
- <exclude name="**/soap/port/*/*PortSoapBindingStub.class" />
- <exclude name="**/soap/port/*/*PortServiceLocator.class" />
- <exclude name="**/soap/port/*/*PortService.class" />
- </fileset>
- </cobertura-instrument>
-
- </target>
-
- <target name="global-cobertura-report" depends="global-taskdefs"
- unless="test.no.cobertura">
- <cobertura-report
- format="html"
- datafile="${global.dir.build.cobertura}/cobertura.ser"
- destdir="${global.dir.build.reports}/cobertura"
- srcdir="${global.dir.build.java}" />
- </target>
-
-
-</project>
diff --git a/etc/targets/global-compile.xml b/etc/targets/global-compile.xml
deleted file mode 100644
index b226041..0000000
--- a/etc/targets/global-compile.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-compile" basedir=".">
-
- <target name="global-compile" depends="global-vpp">
- <mkdir dir="${global.dir.build.classes}" />
- <mkdir dir="${global.dir.build.depcache}" />
- <mkdir dir="${global.dir.build.java}" />
-
- <depend
- srcdir="${global.dir.build.java}"
- destdir="${global.dir.build.classes}"
- cache="${global.dir.build.depcache}"
- closure="yes">
- </depend>
-
- <javac
- srcdir="${global.dir.build.java}"
- destdir="${global.dir.build.classes}"
- optimize="${system.javac.optimize}"
- debug="${system.javac.debug}"
- source="${system.javac.source}"
- target="${system.javac.target}"
- deprecation="${system.javac.deprecation}"
- >
- <classpath refid="compile-classpath" />
- <!--
- <compilerarg compiler="modern" value="-Xlint:unchecked" />
- -->
- <compilerarg compiler="modern" value="-Xlint:deprecation" />
- <compilerarg compiler="modern" value="-Xlint:finally" />
- </javac>
- </target>
-
-</project>
diff --git a/etc/targets/global-findbugs.xml b/etc/targets/global-findbugs.xml
deleted file mode 100644
index 9a06996..0000000
--- a/etc/targets/global-findbugs.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-findbugs" basedir=".">
-
- <!-- Used to depend on compile. -->
- <target name="global-findbugs" depends="global-taskdefs">
- <delete dir="${global.dir.build.reports}/findbugs-output" />
- <mkdir dir="${global.dir.build.reports}/findbugs-output" />
- <findbugs
- home="${global.dir.lib}/findbugs"
- output="xml"
- outputFile="${global.dir.build.reports}/findbugs-output/findbugs.xml" >
- <sourcePath path="${global.dir.build.java}" />
- <class location="${global.dir.build.classes}" />
- <auxClasspath refid="compile-classpath" />
- </findbugs>
-
- </target>
-
- <target name="global-findbugs-report" depends="global-taskdefs">
- <xslt
- basedir="${global.dir.build.reports}/findbugs-output"
- destdir="${global.dir.build.reports}/findbugs"
- extension=".html"
- style="${global.dir.lib}/findbugs/xsl/plain.xsl">
- </xslt>
- </target>
-
- <target name="global-findbugs-gui" depends="global-taskdefs">
- <java
- fork="true"
- spawn="true"
- classname="edu.umd.cs.findbugs.gui.FindBugsFrame">
- <classpath>
- <fileset dir="${global.dir.lib}/findbugs/lib" />
- </classpath>
- <sysproperty
- key="findbugs.home"
- value="${global.dir.lib}/findbugs" />
- <jvmarg value="-Xmx768m" />
- <arg value="-loadbugs" />
- <arg value="${global.dir.build.reports}/findbugs-output/findbugs.xml" />
- </java>
- </target>
-
-
-</project>
diff --git a/etc/targets/global-inject.xml b/etc/targets/global-inject.xml
deleted file mode 100644
index e707b04..0000000
--- a/etc/targets/global-inject.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-inject" basedir=".">
-
- <target name="global-inject">
- <inject>
- <fileset dir="${global.dir.build.classes}">
- <include name="**/*.class" />
- </fileset>
- <criteria>
- <!-- <includeClass class="org.jboss.util.stream.*"/> -->
- <!-- <excludeClass class="java.lang.reflect.*"/> -->
- <!-- <excludeMethod method="get*"/> -->
- <!-- <excludeMethod method="set*"/> -->
- </criteria>
- </inject>
-
- </target>
-
-</project>
diff --git a/etc/targets/global-jar.xml b/etc/targets/global-jar.xml
deleted file mode 100644
index c84dd2a..0000000
--- a/etc/targets/global-jar.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-jar" basedir=".">
-
- <target name="global-jar" depends="global-compile">
- <delete dir="${global.dir.build.jar}" />
- <mkdir dir="${global.dir.build.jar}" />
- <mkdir dir="${global.dir.build.tar}/lib" />
- <mkdir dir="${global.dir.src.resources}" />
-
- <copy todir="${global.dir.build.jar}">
- <fileset dir="${global.dir.build.java}">
- <include name="**/*.dat" />
- </fileset>
- <fileset dir="${global.dir.build.classes}">
- <include name="**" />
- </fileset>
- <fileset dir="${global.dir.src.resources}">
- <include name="org/**" />
- </fileset>
- <!--
- <fileset dir="${global.dir.src.resources}">
- <include name="log4j.properties" />
- </fileset>
- -->
- </copy>
-
- <jar
- destfile="${global.file.jar}"
- manifest="etc/MANIFEST"
- index="true"
- compress="${system.jar.compress}">
- <fileset dir="${global.dir.build.jar}" />
- <manifest>
- <attribute name="Built-By" value="${user.name}" />
- </manifest>
- </jar>
- </target>
-
-</project>
diff --git a/etc/targets/global-javadoc.xml b/etc/targets/global-javadoc.xml
deleted file mode 100644
index 0de8285..0000000
--- a/etc/targets/global-javadoc.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-javadoc" basedir=".">
-
- <target name="global-javadoc" depends="global-vpp" unless="build.no.javadoc">
- <javadoc
- sourcepath="${global.dir.build.java}:${global.dir.src.docs}/javadoc"
- destdir="${global.dir.build.javadoc}"
- classpathref="compile-classpath"
- source="${system.javac.source}"
- access="${system.javadoc.access}"
- use="true"
- splitindex="false"
- failonerror="true"
- windowtitle="Anarres C Preprocessor version ${global.version}"
- >
-
- <package name="org.anarres.cpp" />
- <packageset dir="${global.dir.build.java}" />
-
- <link
- href="http://java.sun.com/j2se/1.5.0/docs/api/"
- offline="${system.javadoc.offline}" />
- <link
- href="http://java.sun.com/j2ee/1.4/docs/api/"
- offline="${system.javadoc.offline}" />
- <link
- href="http://java.sun.com/products/servlet/2.3/javadoc/"
- offline="${system.javadoc.offline}" />
- <link
- href="http://ws.apache.org/axis/java/apiDocs/"
- offline="${system.javadoc.offline}" />
- <!--
- <link
- href="http://www.jdom.org/docs/apidocs/"
- offline="${system.javadoc.offline}" />
- -->
- <link
- href="http://www.junit.org/junit/javadoc/3.8.1/"
- offline="${system.javadoc.offline}" />
- <link
- href="http://jakarta.apache.org/commons/configuration/apidocs/"
- offline="${system.javadoc.offline}" />
- <link
- href="http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/manual/api/"
- offline="${system.javadoc.offline}" />
-<!-- href="http://www.cs.bris.ac.uk/Teaching/Resources/General/ant/docs/manual/api/" -->
- <tag dir="${global.dir.etc}/javadoc" />
-
- <bottom><![CDATA[Copyright &#169; 2007 <a href="http://www.anarres.org/">Shevek, Anarres</a>. All Rights Reserved.]]></bottom>
- </javadoc>
-
- </target>
-
-</project>
diff --git a/etc/targets/global-junit.xml b/etc/targets/global-junit.xml
deleted file mode 100644
index e53c5a7..0000000
--- a/etc/targets/global-junit.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-junit" basedir=".">
-
- <target name="global-junit" depends="global-taskdefs">
- <runtarget target="global-junit-compile" />
- <runtarget target="global-cobertura-instrument" />
-
- <mkdir dir="${global.dir.build.reports}/junit-output" />
- <!-- This only sets the properties if they aren't set
- - already. -->
- <property name="test.package" value="" />
- <property name="test.case" value="" />
- <property name="test.all" value="" />
- <property name="test.root" value="${global.dir.build.tests}"/>
- <property name="jrat.configuration" value="${global.dir.src.resources}/jrat.xml" />
-
- <runtarget target="global-junit-cleanup" />
-
- <junit
- printsummary="yes"
- showoutput="yes"
- haltonfailure="no"
- fork="yes"
- forkmode="once"
- >
- <jvmarg value="-Xbootclasspath/p:${global.dir.build}/hacks/lib/boot.jar"/>
- <classpath refid="junit-run-classpath"/>
- <formatter type="plain" />
- <formatter type="xml" />
- <sysproperty key="net.sourceforge.cobertura.datafile"
- file="${global.dir.build.cobertura}/cobertura.ser" />
- <sysproperty key="test.package" value="${test.package}" />
- <sysproperty key="test.case" value="${test.case}" />
- <sysproperty key="test.root" value="${test.root}" />
- <sysproperty key="test.all" value="${test.all}" />
- <sysproperty key="global.dir.build.classes"
- value="${global.dir.build.classes}" />
- <sysproperty key="jrat.configuration" value="${jrat.configuration}" />
-
- <assertions>
- <enable/>
- </assertions>
-
- <batchtest fork="yes" todir="${global.dir.build.reports}/junit-output">
- <fileset dir="${global.dir.build.tests}">
- <include name="AutoTestSuite.class" />
- </fileset>
- </batchtest>
- </junit>
-
- <delete dir="${global.dir.root}/webroot" />
- <delete dir="${global.dir.src.resources}/webapp/WEB-INF" />
-
-
- <runtarget target="global-junit-report" />
- <runtarget target="global-cobertura-report" />
- </target>
-
- <target name="global-junit-cleanup">
- <delete dir="${global.dir.root}/webroot" />
- </target>
-
- <target name="global-junit-compile">
- <delete dir="${global.dir.build.tests}" />
- <mkdir dir="${global.dir.src.tests}" />
- <mkdir dir="${global.dir.build.tests}" />
-
- <javac
- srcdir="${global.dir.src.tests}"
- destdir="${global.dir.build.tests}"
- optimize="false"
- debug="true"
- source="${system.javac.source}"
- target="${system.javac.target}"
- deprecation="${system.javac.deprecation}"
- classpathref="junit-compile-classpath"
- >
- <!--
- <exclude name="**/master/**" />
- <exclude name="**/slave/**" />
- <exclude name="**/old/**" />
- -->
- </javac>
- </target>
-
- <target name="global-junit-report" depends="global-taskdefs">
- <junitreport
- todir="${global.dir.build.reports}/junit-output">
- <fileset dir="${global.dir.build.reports}/junit-output">
- <include name="*.xml" />
- <exclude name="TESTS-TestSuites.xml" />
- </fileset>
- <report format="frames" todir="${global.dir.build.reports}/junit"
- styledir="${global.dir.etc}/junit/xsl"/>
- </junitreport>
- </target>
-
-</project>
diff --git a/etc/targets/global-tar.xml b/etc/targets/global-tar.xml
deleted file mode 100644
index 778cf2f..0000000
--- a/etc/targets/global-tar.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-tar" basedir=".">
-
- <target name="global-tar-dir" depends="global-jar">
- <mkdir dir="${global.dir.build.dist}" />
- <mkdir dir="${global.dir.build.tar}/lib" />
- <mkdir dir="${global.dir.build.tar}/bin" />
- <mkdir dir="${global.dir.build.javadoc}" />
-
- <copy todir="${global.dir.build.tar}/lib">
- <!--
- <fileset dir="${global.dir.lib}/log4j">
- <include name="**/*.jar" />
- </fileset>
- -->
- <fileset dir="${global.dir.lib}/runtime">
- <include name="**/*.jar" />
- </fileset>
- <mapper type="flatten" />
- </copy>
-
- <copy todir="${global.dir.build.tar}">
- <fileset dir="${global.dir.src.scripts}">
- <include name="jcpp" />
- </fileset>
- <fileset dir="${global.dir.root}">
- <include name="LICENSE" />
- </fileset>
- </copy>
-
- <chmod perm="a+x">
- <fileset dir="${global.dir.build.tar}/bin">
- <include name="**/*.pl"/>
- </fileset>
- <fileset dir="${global.dir.build.tar}">
- <include name="*.sh"/>
- </fileset>
- <fileset dir="${global.dir.build.tar}">
- <include name="jcpp"/>
- </fileset>
- </chmod>
-
- <!-- <runtarget target="global-inject" /> -->
- </target>
-
- <target name="global-tar" depends="global-tar-dir,global-javadoc">
-
- <copy todir="${global.dir.build.tar}/docs/api">
- <fileset dir="${global.dir.build.javadoc}" />
- </copy>
-
- <tar
- destfile="${global.file.bintar}"
- compression="gzip"
- longfile="gnu">
- <tarfileset
- dir="${global.dir.build.tar}"
- prefix="${global.name}-bin-${global.version}"
- username="root" group="root"
- uid="0" gid="0"
- >
- <include name="**" />
- </tarfileset>
- </tar>
-
- <tar
- destfile="${global.file.srctar}"
- compression="gzip"
- longfile="gnu">
- <tarfileset
- dir="${global.dir.root}"
- prefix="${global.name}-src-${global.version}"
- username="root" group="root"
- uid="0" gid="0"
- >
- <include name="**" />
- <exclude name="build/**" />
- <exclude name="**/.*.swp" />
- <exclude name="src/scripts/release.sh" />
- </tarfileset>
- </tar>
-
- </target>
-
-</project>
diff --git a/etc/targets/global-taskdefs.xml b/etc/targets/global-taskdefs.xml
deleted file mode 100644
index 8218c91..0000000
--- a/etc/targets/global-taskdefs.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-taskdefs">
-
- <target name="global-taskdefs" unless="global.taskdefs.done">
-
- <taskdef
- resource="net/sf/antcontrib/antlib.xml"
- classpathref="antcontrib-classpath"
- loaderref="ant" />
-
- <taskdef
- resource="net/sf/antcontrib/antcontrib.properties"
- classpathref="antcontrib-classpath"
- loaderref="ant" />
-
- <taskdef
- resource="checkstyletask.properties"
- classpathref="checkstyle-classpath"
- loaderref="checkstyle"
- />
-
- <taskdef
- resource="foundrylogic/vpp/taskdef.properties"
- classpathref="vpp-classpath"
- loaderref="vpp" />
-
- <!--
- velocity-tools-generic-1.4.jar
- jtidy-r8-21122004.jar
- -->
-
- <taskdef name="injectcopy"
- classname="org.shiftone.jrat.inject.ant.InjectCopyTask"
- classpathref="jrat-classpath"
- loaderref="jrat" />
-
- <taskdef name="inject"
- classname="org.shiftone.jrat.inject.ant.InjectTask"
- classpathref="jrat-classpath"
- loaderref="jrat" />
-
- <taskdef
- classpathref="cobertura-classpath"
- resource="tasks.properties"
- loaderref="cobratura"
- />
-
- <taskdef name="findbugs"
- classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
- classpathref="findbugs-classpath"
- loaderref="findbugs" />
-
- <taskdef
- name="svn"
- classname="org.tigris.subversion.svnant.SvnTask"
- classpathref="svn-classpath"
- loaderref="svn"
- />
-
- <property name="global.taskdefs.done" value="true" />
-
- </target>
-
-</project>
diff --git a/etc/targets/global-verify.xml b/etc/targets/global-verify.xml
deleted file mode 100644
index 11c8674..0000000
--- a/etc/targets/global-verify.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-verify" basedir=".">
-
- <target name="global-verify" depends="global-taskdefs">
- <!-- "master-jar,slave-jar,client-jar" -->
- <mkdir dir="${global.dir.build.reports}" />
-
- <verifydesign
- design="${global.dir.etc}/design.xml"
- jar="${master.file.jar}" />
-
- <verifydesign
- design="${global.dir.etc}/design.xml"
- jar="${slave.file.jar}" />
-
- <verifydesign
- design="${global.dir.etc}/design.xml"
- jar="${client.file.jar}" />
-
- </target>
-
-</project>
diff --git a/etc/targets/global-vpp.xml b/etc/targets/global-vpp.xml
deleted file mode 100644
index 6ffc23d..0000000
--- a/etc/targets/global-vpp.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<project name="global-vpp">
-
- <target name="global-vpp" depends="global-taskdefs">
- <mkdir dir="${global.dir.build.java}" />
-
- <vppcopy todir="${global.dir.build.java}">
- <fileset dir="${global.dir.src.java}">
- <include name="**/*.java" />
- </fileset>
- <config>
- <context>
- <!-- Avoid subversion and RCS tags. -->
- <property key="Revision" value="\$Revision" />
- <property key="Id" value="\$Id" />
- <property key="Author" value="\$Author" />
- <property key="Date" value="\$Date" />
- <property key="URL" value="\$URL" />
- <property key="Rev" value="\$Rev" />
- <property key="SOA" value="\$SOA" />
- <property key="TTL" value="\$TTL" />
- <property key="DATASET" value="$DATASET" />
- <property key="_" value="\$_" />
-
- <property key="version" value="${global.version}" />
- </context>
- </config>
- </vppcopy>
- </target>
-
-</project>
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..f271e55
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+version=1.4.0-SNAPSHOT
diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle
new file mode 100644
index 0000000..3163aa9
--- /dev/null
+++ b/gradle/buildscript.gradle
@@ -0,0 +1,11 @@
+// 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
new file mode 100644
index 0000000..78f8180
--- /dev/null
+++ b/gradle/check.gradle
@@ -0,0 +1,25 @@
+// 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
new file mode 100644
index 0000000..22023ea
--- /dev/null
+++ b/gradle/convention.gradle
@@ -0,0 +1,88 @@
+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 = "[email protected]: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
new file mode 100644
index 0000000..59b75b3
--- /dev/null
+++ b/gradle/license.gradle
@@ -0,0 +1,8 @@
+// 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
new file mode 100644
index 0000000..0513719
--- /dev/null
+++ b/gradle/maven.gradle
@@ -0,0 +1,69 @@
+// 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'
+ }
+ }
+ 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:[email protected]:shevek/${rootProject.githubProjectName}.git"
+ url "scm:git:[email protected]:shevek/${rootProject.githubProjectName}.git"
+ developerConnection "scm:git:[email protected]: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
new file mode 100644
index 0000000..a87bc54
--- /dev/null
+++ b/gradle/netflix-oss.gradle
@@ -0,0 +1 @@
+apply from: 'http://artifacts.netflix.com/gradle-netflix-local/artifactory.gradle'
diff --git a/gradle/release.gradle b/gradle/release.gradle
new file mode 100644
index 0000000..6e3c20e
--- /dev/null
+++ b/gradle/release.gradle
@@ -0,0 +1,60 @@
+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
new file mode 100644
index 0000000..5838598
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..40aa5f8
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#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
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/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
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@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/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..f22856c
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name='jcpp'
diff --git a/src/docs/javadoc/.gitignore b/src/docs/javadoc/.gitignore
deleted file mode 100644
index e69de29..0000000
--- a/src/docs/javadoc/.gitignore
+++ /dev/null
diff --git a/src/java/org/anarres/cpp/ChrootFileSystem.java b/src/java/org/anarres/cpp/ChrootFileSystem.java
deleted file mode 100644
index bce2cfe..0000000
--- a/src/java/org/anarres/cpp/ChrootFileSystem.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * A virtual filesystem implementation using java.io in a virtual
- * chroot.
- */
-public class ChrootFileSystem implements VirtualFileSystem {
- private File root;
-
- public ChrootFileSystem(File root) {
- this.root = root;
- }
-
- public VirtualFile getFile(String path) {
- return new ChrootFile(path);
- }
-
- public VirtualFile getFile(String dir, String name) {
- return new ChrootFile(dir, name);
- }
-
- private class ChrootFile extends File implements VirtualFile {
- private File rfile;
-
- public ChrootFile(String path) {
- super(path);
- }
-
- public ChrootFile(String dir, String name) {
- super(dir, name);
- }
-
- /* private */
- public ChrootFile(File dir, String name) {
- super(dir, name);
- }
-
- @Override
- public ChrootFile getParentFile() {
- return new ChrootFile(getParent());
- }
-
- public ChrootFile getChildFile(String name) {
- return new ChrootFile(this, name);
- }
-
- @Override
- public boolean isFile() {
- File real = new File(root, getPath());
- return real.isFile();
- }
-
- public Source getSource() throws IOException {
- return new FileLexerSource(new File(root, getPath()),
- getPath());
- }
- }
-
-}
diff --git a/src/java/org/anarres/cpp/CppReader.java b/src/java/org/anarres/cpp/CppReader.java
deleted file mode 100644
index fbe3545..0000000
--- a/src/java/org/anarres/cpp/CppReader.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.Reader;
-
-import static org.anarres.cpp.Token.*;
-
-/**
- * A Reader wrapper around the Preprocessor.
- *
- * This is a utility class to provide a transparent {@link Reader}
- * which preprocesses the input text.
- *
- * @see Preprocessor
- * @see Reader
- */
-public class CppReader extends Reader {
-
- private Preprocessor cpp;
- private String token;
- private int idx;
-
- public CppReader(final Reader r) {
- cpp = new Preprocessor(new LexerSource(r, true) {
- @Override
- public String getName() {
- return "<CppReader Input@" +
- System.identityHashCode(r) + ">";
- }
- });
- token = "";
- idx = 0;
- }
-
- public CppReader(Preprocessor p) {
- cpp = p;
- token = "";
- idx = 0;
- }
-
- /**
- * Returns the Preprocessor used by this CppReader.
- */
- public Preprocessor getPreprocessor() {
- return cpp;
- }
-
- /**
- * Defines the given name as a macro.
- *
- * This is a convnience method.
- */
- public void addMacro(String name)
- throws LexerException {
- cpp.addMacro(name);
- }
-
- /**
- * Defines the given name as a macro.
- *
- * This is a convnience method.
- */
- public void addMacro(String name, String value)
- throws LexerException {
- cpp.addMacro(name, value);
- }
-
- private boolean refill()
- throws IOException {
- try {
- assert cpp != null : "cpp is null : was it closed?";
- if (token == null)
- return false;
- while (idx >= token.length()) {
- Token tok = cpp.token();
- switch (tok.getType()) {
- case EOF:
- token = null;
- return false;
- case CCOMMENT:
- case CPPCOMMENT:
- if (!cpp.getFeature(Feature.KEEPCOMMENTS)) {
- token = " ";
- break;
- }
- default:
- token = tok.getText();
- break;
- }
- idx = 0;
- }
- return true;
- }
- catch (LexerException e) {
- /* Never happens.
- if (e.getCause() instanceof IOException)
- throw (IOException)e.getCause();
- */
- IOException ie = new IOException(String.valueOf(e));
- ie.initCause(e);
- throw ie;
- }
- }
-
- public int read()
- throws IOException {
- if (!refill())
- return -1;
- return token.charAt(idx++);
- }
-
- /* XXX Very slow and inefficient. */
- public int read(char cbuf[], int off, int len)
- throws IOException {
- if (token == null)
- return -1;
- for (int i = 0; i < len; i++) {
- int ch = read();
- if (ch == -1)
- return i;
- cbuf[off + i] = (char)ch;
- }
- return len;
- }
-
- public void close()
- throws IOException {
- if (cpp != null) {
- cpp.close();
- cpp = null;
- }
- token = null;
- }
-
-}
diff --git a/src/java/org/anarres/cpp/CppTask.java b/src/java/org/anarres/cpp/CppTask.java
deleted file mode 100644
index 05fc1f9..0000000
--- a/src/java/org/anarres/cpp/CppTask.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.taskdefs.Copy;
-import org.apache.tools.ant.types.FileSet;
-import org.apache.tools.ant.types.FilterChain;
-import org.apache.tools.ant.types.FilterSet;
-import org.apache.tools.ant.types.FilterSetCollection;
-import org.apache.tools.ant.types.Path;
-
-import org.anarres.cpp.LexerException;
-import org.anarres.cpp.Preprocessor;
-import org.anarres.cpp.PreprocessorListener;
-import org.anarres.cpp.Token;
-
-/**
- * An ant task for jcpp.
- */
-public class CppTask extends Copy {
-
- private class Listener extends PreprocessorListener {
- protected void print(String msg) {
- log(msg);
- }
- }
-
- public static class Macro {
- private String name;
- private String value;
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- public String getValue() {
- return value;
- }
- }
-
- private final Listener listener = new Listener();
- private final List<Macro> macros = new ArrayList<Macro>();
- private Path systemincludepath;
- private Path localincludepath;
-
- public void addMacro(Macro macro) {
- macros.add(macro);
- }
-
- public void addSystemincludepath(Path path) {
- if (systemincludepath == null)
- systemincludepath = new Path(getProject());
- systemincludepath.add(path);
- }
-
- public void addLocalincludepath(Path path) {
- if (localincludepath == null)
- localincludepath = new Path(getProject());
- localincludepath.add(path);
- }
-
-/*
- public void execute() {
- FileWriter writer = null;
- try {
- if (input == null)
- throw new BuildException("Input not specified");
- if (output == null)
- throw new BuildException("Output not specified");
- cpp.addInput(this.input);
- writer = new FileWriter(this.output);
- for (;;) {
- Token tok = cpp.token();
- if (tok != null && tok.getType() == Token.EOF)
- break;
- writer.write(tok.getText());
- }
- }
- catch (Exception e) {
- throw new BuildException(e);
- }
- finally {
- if (writer != null) {
- try {
- writer.close();
- }
- catch (IOException e) {
- }
- }
- }
- }
-*/
-
- private void preprocess(File input, File output) throws Exception {
- Preprocessor cpp = new Preprocessor();
- cpp.setListener(listener);
- for (Macro macro : macros)
- cpp.addMacro(macro.getName(), macro.getValue());
- if (systemincludepath != null)
- cpp.setSystemIncludePath(Arrays.asList(systemincludepath.list()));
- if (localincludepath != null)
- cpp.setQuoteIncludePath(Arrays.asList(localincludepath.list()));
-
- File dir = output.getParentFile();
- if (!dir.exists()) {
- if (!dir.mkdirs())
- throw new BuildException("Failed to make parent directory " + dir);
- } else if (!dir.isDirectory()) {
- throw new BuildException("Parent directory of output file " + output + " exists, but is not a directory.");
- }
- FileWriter writer = null;
- try {
- if (input == null)
- throw new BuildException("Input not specified");
- if (output == null)
- throw new BuildException("Output not specified");
- cpp.addInput(input);
- writer = new FileWriter(output);
- for (;;) {
- Token tok = cpp.token();
- if (tok != null && tok.getType() == Token.EOF)
- break;
- writer.write(tok.getText());
- }
- }
- finally {
- if (writer != null) {
- try {
- writer.close();
- }
- catch (IOException e) {
- }
- }
- }
- }
-
- protected void doFileOperations() {
- if(fileCopyMap.size() > 0) {
- log("Copying " + fileCopyMap.size()
- + " file" + (fileCopyMap.size() == 1 ? "" : "s")
- + " to " + destDir.getAbsolutePath());
-
- Enumeration e = fileCopyMap.keys();
-
- while (e.hasMoreElements()) {
- String fromFile = (String)e.nextElement();
- String[] toFiles = (String[])fileCopyMap.get(fromFile);
-
- for(int i = 0; i < toFiles.length; i++) {
- String toFile = toFiles[i];
-
- if(fromFile.equals(toFile)) {
- log("Skipping self-copy of " + fromFile, verbosity);
- continue;
- }
-
- try {
- log("Copying " + fromFile + " to " + toFile, verbosity);
-
- FilterSetCollection executionFilters =
- new FilterSetCollection();
- if(filtering) {
- executionFilters
- .addFilterSet(getProject().getGlobalFilterSet());
- }
- for(Enumeration filterEnum = getFilterSets().elements();
- filterEnum.hasMoreElements();) {
- executionFilters
- .addFilterSet((FilterSet)filterEnum.nextElement());
- }
-
- File srcFile = new File(fromFile);
- File dstFile = new File(toFile);
- preprocess(srcFile, dstFile);
- }
- catch(Exception ioe) {
- ioe.printStackTrace();
- String msg = "Failed to copy " + fromFile + " to " + toFile
- + " due to " + ioe.getMessage();
- File targetFile = new File(toFile);
- if(targetFile.exists() && !targetFile.delete()) {
- msg += " and I couldn't delete the corrupt " + toFile;
- }
- throw new BuildException(msg, ioe, getLocation());
- }
- }
- }
- }
-
- }
-
-
-}
diff --git a/src/java/org/anarres/cpp/FixedTokenSource.java b/src/java/org/anarres/cpp/FixedTokenSource.java
deleted file mode 100644
index eb19685..0000000
--- a/src/java/org/anarres/cpp/FixedTokenSource.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PushbackReader;
-import java.io.Reader;
-import java.io.StringReader;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Iterator;
-
-/* pp */ class FixedTokenSource extends Source {
- private static final Token EOF =
- new Token(Token.EOF, "<ts-eof>");
-
- private List<Token> tokens;
- private int idx;
-
- /* pp */ FixedTokenSource(Token... tokens) {
- this.tokens = Arrays.asList(tokens);
- this.idx = 0;
- }
-
- /* pp */ FixedTokenSource(List<Token> tokens) {
- this.tokens = tokens;
- this.idx = 0;
- }
-
- public Token token()
- throws IOException,
- LexerException {
- if (idx >= tokens.size())
- return EOF;
- return tokens.get(idx++);
- }
-
- public String toString() {
- StringBuilder buf = new StringBuilder();
- buf.append("constant token stream " + tokens);
- Source parent = getParent();
- if (parent != null)
- buf.append(" in ").append(String.valueOf(parent));
- return buf.toString();
- }
-}
diff --git a/src/java/org/anarres/cpp/JavaFileSystem.java b/src/java/org/anarres/cpp/JavaFileSystem.java
deleted file mode 100644
index 83a5caa..0000000
--- a/src/java/org/anarres/cpp/JavaFileSystem.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * A virtual filesystem implementation using java.io.
- */
-public class JavaFileSystem implements VirtualFileSystem {
- public VirtualFile getFile(String path) {
- return new JavaFile(path);
- }
-
- public VirtualFile getFile(String dir, String name) {
- return new JavaFile(dir, name);
- }
-
- private class JavaFile extends File implements VirtualFile {
- public JavaFile(String path) {
- super(path);
- }
-
- public JavaFile(String dir, String name) {
- super(dir, name);
- }
-
- /* private */
- public JavaFile(File dir, String name) {
- super(dir, name);
- }
-
-/*
- @Override
- public String getPath() {
- return getCanonicalPath();
- }
-*/
-
- @Override
- public JavaFile getParentFile() {
- String parent = getParent();
- if (parent != null)
- return new JavaFile(parent);
- File absolute = getAbsoluteFile();
- parent = absolute.getParent();
- /*
- if (parent == null)
- return null;
- */
- return new JavaFile(parent);
- }
-
- public JavaFile getChildFile(String name) {
- return new JavaFile(this, name);
- }
-
- public Source getSource() throws IOException {
- return new FileLexerSource(this);
- }
-
- }
-
-}
diff --git a/src/java/org/anarres/cpp/JoinReader.java b/src/java/org/anarres/cpp/JoinReader.java
deleted file mode 100644
index 91908a7..0000000
--- a/src/java/org/anarres/cpp/JoinReader.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.Reader;
-import java.io.PushbackReader;
-import java.io.IOException;
-
-/* pp */ class JoinReader /* extends Reader */ {
- private Reader in;
-
- private PreprocessorListener listener;
- private LexerSource source;
- private boolean trigraphs;
- private boolean warnings;
-
- private int newlines;
- private boolean flushnl;
- private int[] unget;
- private int uptr;
-
- public JoinReader(Reader in, boolean trigraphs) {
- this.in = in;
- this.trigraphs = trigraphs;
- this.newlines = 0;
- this.flushnl = false;
- this.unget = new int[2];
- this.uptr = 0;
- }
-
- public JoinReader(Reader in) {
- this(in, false);
- }
-
- public void setTrigraphs(boolean enable, boolean warnings) {
- this.trigraphs = enable;
- this.warnings = warnings;
- }
-
- /* pp */ void init(Preprocessor pp, LexerSource s) {
- this.listener = pp.getListener();
- this.source = s;
- setTrigraphs(pp.getFeature(Feature.TRIGRAPHS),
- pp.getWarning(Warning.TRIGRAPHS));
- }
-
- private int __read() throws IOException {
- if (uptr > 0)
- return unget[--uptr];
- return in.read();
- }
-
- private void _unread(int c) {
- if (c != -1)
- unget[uptr++] = c;
- assert uptr <= unget.length :
- "JoinReader ungets too many characters";
- }
-
- protected void warning(String msg)
- throws LexerException {
- if (source != null)
- source.warning(msg);
- else
- throw new LexerException(msg);
- }
-
- private char trigraph(char raw, char repl)
- throws IOException, LexerException {
- if (trigraphs) {
- if (warnings)
- warning("trigraph ??" + raw + " converted to " + repl);
- return repl;
- }
- else {
- if (warnings)
- warning("trigraph ??" + raw + " ignored");
- _unread(raw);
- _unread('?');
- return '?';
- }
- }
-
- private int _read()
- throws IOException, LexerException {
- int c = __read();
- if (c == '?' && (trigraphs || warnings)) {
- int d = __read();
- if (d == '?') {
- int e = __read();
- switch (e) {
- case '(': return trigraph('(', '[');
- case ')': return trigraph(')', ']');
- case '<': return trigraph('<', '{');
- case '>': return trigraph('>', '}');
- case '=': return trigraph('=', '#');
- case '/': return trigraph('/', '\\');
- case '\'': return trigraph('\'', '^');
- case '!': return trigraph('!', '|');
- case '-': return trigraph('-', '~');
- }
- _unread(e);
- }
- _unread(d);
- }
- return c;
- }
-
- public int read()
- throws IOException, LexerException {
- if (flushnl) {
- if (newlines > 0) {
- newlines--;
- return '\n';
- }
- flushnl = false;
- }
-
- for (;;) {
- int c = _read();
- switch (c) {
- case '\\':
- int d = _read();
- switch (d) {
- case '\n':
- newlines++;
- continue;
- case '\r':
- newlines++;
- int e = _read();
- if (e != '\n')
- _unread(e);
- continue;
- default:
- _unread(d);
- return c;
- }
- case '\r':
- case '\n':
- case '\u2028':
- case '\u2029':
- case '\u000B':
- case '\u000C':
- case '\u0085':
- flushnl = true;
- return c;
- case -1:
- if (newlines > 0) {
- newlines--;
- return '\n';
- }
- default:
- return c;
- }
- }
- }
-
- public int read(char cbuf[], int off, int len)
- throws IOException, LexerException {
- for (int i = 0; i < len; i++) {
- int ch = read();
- if (ch == -1)
- return i;
- cbuf[off + i] = (char)ch;
- }
- return len;
- }
-
- public void close()
- throws IOException {
- in.close();
- }
-
- public String toString() {
- return "JoinReader(nl=" + newlines + ")";
- }
-
-/*
- public static void main(String[] args) throws IOException {
- FileReader f = new FileReader(new File(args[0]));
- BufferedReader b = new BufferedReader(f);
- JoinReader r = new JoinReader(b);
- BufferedWriter w = new BufferedWriter(
- new java.io.OutputStreamWriter(System.out)
- );
- int c;
- while ((c = r.read()) != -1) {
- w.write((char)c);
- }
- w.close();
- }
-*/
-
-}
diff --git a/src/java/org/anarres/cpp/LexerSource.java b/src/java/org/anarres/cpp/LexerSource.java
deleted file mode 100644
index 5f3bfc7..0000000
--- a/src/java/org/anarres/cpp/LexerSource.java
+++ /dev/null
@@ -1,893 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PushbackReader;
-import java.io.Reader;
-
-import java.util.Set;
-
-import static org.anarres.cpp.Token.*;
-
-/** Does not handle digraphs. */
-public class LexerSource extends Source {
- private static final boolean DEBUG = false;
-
- private JoinReader reader;
- private boolean ppvalid;
- private boolean bol;
- private boolean include;
-
- private boolean digraphs;
-
- /* Unread. */
- private int u0, u1;
- private int ucount;
-
- private int line;
- private int column;
- private int lastcolumn;
- private boolean cr;
-
- /* ppvalid is:
- * false in StringLexerSource,
- * true in FileLexerSource */
- public LexerSource(Reader r, boolean ppvalid) {
- this.reader = new JoinReader(r);
- this.ppvalid = ppvalid;
- this.bol = true;
- this.include = false;
-
- this.digraphs = true;
-
- this.ucount = 0;
-
- this.line = 1;
- this.column = 0;
- this.lastcolumn = -1;
- this.cr = false;
- }
-
- @Override
- /* pp */ void init(Preprocessor pp) {
- super.init(pp);
- this.digraphs = pp.getFeature(Feature.DIGRAPHS);
- this.reader.init(pp, this);
- }
-
- @Override
- public int getLine() {
- return line;
- }
-
- @Override
- public int getColumn() {
- return column;
- }
-
- @Override
- /* pp */ boolean isNumbered() {
- return true;
- }
-
-/* Error handling. */
-
- private final void _error(String msg, boolean error)
- throws LexerException {
- int _l = line;
- int _c = column;
- if (_c == 0) {
- _c = lastcolumn;
- _l--;
- }
- else {
- _c--;
- }
- if (error)
- super.error(_l, _c, msg);
- else
- super.warning(_l, _c, msg);
- }
-
- /* Allow JoinReader to call this. */
- /* pp */ final void error(String msg)
- throws LexerException {
- _error(msg, true);
- }
-
- /* Allow JoinReader to call this. */
- /* pp */ final void warning(String msg)
- throws LexerException {
- _error(msg, false);
- }
-
-/* A flag for string handling. */
-
- /* pp */ void setInclude(boolean b) {
- this.include = b;
- }
-
-/*
- private boolean _isLineSeparator(int c) {
- return Character.getType(c) == Character.LINE_SEPARATOR
- || c == -1;
- }
-*/
-
- /* XXX Move to JoinReader and canonicalise newlines. */
- private static final boolean isLineSeparator(int c) {
- switch ((char)c) {
- case '\r':
- case '\n':
- case '\u2028':
- case '\u2029':
- case '\u000B':
- case '\u000C':
- case '\u0085':
- return true;
- default:
- return (c == -1);
- }
- }
-
-
- private int read()
- throws IOException,
- LexerException {
- int c;
- assert ucount <= 2 : "Illegal ucount: " + ucount;
- switch (ucount) {
- case 2:
- ucount = 1;
- c = u1;
- break;
- case 1:
- ucount = 0;
- c = u0;
- break;
- default:
- if (reader == null)
- c = -1;
- else
- c = reader.read();
- break;
- }
-
- switch (c) {
- case '\r':
- cr = true;
- line++;
- lastcolumn = column;
- column = 0;
- break;
- case '\n':
- if (cr) {
- cr = false;
- break;
- }
- /* fallthrough */
- case '\u2028':
- case '\u2029':
- case '\u000B':
- case '\u000C':
- case '\u0085':
- cr = false;
- line++;
- lastcolumn = column;
- column = 0;
- break;
- case -1:
- cr = false;
- break;
- default:
- cr = false;
- column++;
- break;
- }
-
-/*
- if (isLineSeparator(c)) {
- line++;
- lastcolumn = column;
- column = 0;
- }
- else {
- column++;
- }
-*/
-
- return c;
- }
-
- /* You can unget AT MOST one newline. */
- private void unread(int c)
- throws IOException {
- /* XXX Must unread newlines. */
- if (c != -1) {
- if (isLineSeparator(c)) {
- line--;
- column = lastcolumn;
- cr = false;
- }
- else {
- column--;
- }
- switch (ucount) {
- case 0:
- u0 = c;
- ucount = 1;
- break;
- case 1:
- u1 = c;
- ucount = 2;
- break;
- default:
- throw new IllegalStateException(
- "Cannot unget another character!"
- );
- }
- // reader.unread(c);
- }
- }
-
- /* Consumes the rest of the current line into an invalid. */
- private Token invalid(StringBuilder text, String reason)
- throws IOException,
- LexerException {
- int d = read();
- while (!isLineSeparator(d)) {
- text.append((char)d);
- d = read();
- }
- unread(d);
- return new Token(INVALID, text.toString(), reason);
- }
-
- private Token ccomment()
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder("/*");
- int d;
- do {
- do {
- d = read();
- text.append((char)d);
- } while (d != '*');
- do {
- d = read();
- text.append((char)d);
- } while (d == '*');
- } while (d != '/');
- return new Token(CCOMMENT, text.toString());
- }
-
- private Token cppcomment()
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder("//");
- int d = read();
- while (!isLineSeparator(d)) {
- text.append((char)d);
- d = read();
- }
- unread(d);
- return new Token(CPPCOMMENT, text.toString());
- }
-
- private int escape(StringBuilder text)
- throws IOException,
- LexerException {
- int d = read();
- switch (d) {
- case 'a': text.append('a'); return 0x07;
- case 'b': text.append('b'); return '\b';
- case 'f': text.append('f'); return '\f';
- case 'n': text.append('n'); return '\n';
- case 'r': text.append('r'); return '\r';
- case 't': text.append('t'); return '\t';
- case 'v': text.append('v'); return 0x0b;
- case '\\': text.append('\\'); return '\\';
-
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- int len = 0;
- int val = 0;
- do {
- val = (val << 3) + Character.digit(d, 8);
- text.append((char)d);
- d = read();
- } while (++len < 3 && Character.digit(d, 8) != -1);
- unread(d);
- return val;
-
- case 'x':
- text.append((char)d);
- len = 0;
- val = 0;
- while (len++ < 2) {
- d = read();
- if (Character.digit(d, 16) == -1) {
- unread(d);
- break;
- }
- val = (val << 4) + Character.digit(d, 16);
- text.append((char)d);
- }
- return val;
-
- /* Exclude two cases from the warning. */
- case '"': text.append('"'); return '"';
- case '\'': text.append('\''); return '\'';
-
- default:
- warning("Unnecessary escape character " + (char)d);
- text.append((char)d);
- return d;
- }
- }
-
- private Token character()
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder("'");
- int d = read();
- if (d == '\\') {
- text.append('\\');
- d = escape(text);
- }
- else if (isLineSeparator(d)) {
- unread(d);
- return new Token(INVALID, text.toString(),
- "Unterminated character literal");
- }
- else if (d == '\'') {
- text.append('\'');
- return new Token(INVALID, text.toString(),
- "Empty character literal");
- }
- else if (!Character.isDefined(d)) {
- text.append('?');
- return invalid(text, "Illegal unicode character literal");
- }
- else {
- text.append((char)d);
- }
-
- int e = read();
- if (e != '\'') {
- // error("Illegal character constant");
- /* We consume up to the next ' or the rest of the line. */
- for (;;) {
- if (isLineSeparator(e)) {
- unread(e);
- break;
- }
- text.append((char)e);
- if (e == '\'')
- break;
- e = read();
- }
- return new Token(INVALID, text.toString(),
- "Illegal character constant " + text);
- }
- text.append('\'');
- /* XXX It this a bad cast? */
- return new Token(CHARACTER,
- text.toString(), Character.valueOf((char)d));
- }
-
- private Token string(char open, char close)
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder();
- text.append(open);
-
- StringBuilder buf = new StringBuilder();
-
- for (;;) {
- int c = read();
- if (c == close) {
- break;
- }
- else if (c == '\\') {
- text.append('\\');
- if (!include) {
- char d = (char)escape(text);
- buf.append(d);
- }
- }
- 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);
- }
- else if (isLineSeparator(c)) {
- unread(c);
- // error("Unterminated string literal after " + buf);
- return new Token(INVALID, text.toString(),
- "Unterminated string literal after " + buf);
- }
- else {
- text.append((char)c);
- buf.append((char)c);
- }
- }
- text.append(close);
- switch (close) {
- case '"':
- return new Token(STRING,
- text.toString(), buf.toString());
- case '>':
- return new Token(HEADER,
- text.toString(), buf.toString());
- case '\'':
- if (buf.length() == 1)
- return new Token(CHARACTER,
- text.toString(), buf.toString());
- return new Token(SQSTRING,
- text.toString(), buf.toString());
- default:
- throw new IllegalStateException(
- "Unknown closing character " + (char)close);
- }
- }
-
- private Token _number_suffix(StringBuilder text, NumericValue value, int d)
- throws IOException,
- LexerException {
- int flags = 0; // U, I, L, LL, F, D, MSB
- for (;;) {
- if (d == 'U' || d == 'u') {
- if ((flags & NumericValue.F_UNSIGNED) != 0)
- warning("Duplicate unsigned suffix " + d);
- flags |= NumericValue.F_UNSIGNED;
- text.append((char)d);
- d = read();
- }
- else if (d == 'L' || d == 'l') {
- if ((flags & NumericValue.FF_SIZE) != 0)
- warning("Nultiple length suffixes after " + text);
- text.append((char)d);
- int e = read();
- if (e == d) { // Case must match. Ll is Welsh.
- flags |= NumericValue.F_LONGLONG;
- text.append((char)e);
- d = read();
- } else {
- flags |= NumericValue.F_LONG;
- d = e;
- }
- }
- else if (d == 'I' || d == 'i') {
- if ((flags & NumericValue.FF_SIZE) != 0)
- warning("Nultiple 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);
- 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);
- flags |= NumericValue.F_DOUBLE;
- text.append((char)d);
- d = read();
- }
- // This should probably be isPunct() || isWhite().
- else if (Character.isLetter(d) || d == '_') {
- unread(d);
- value.setFlags(flags);
- return invalid(text,
- "Invalid suffix \"" + (char)d +
- "\" on numeric constant");
- }
- else {
- unread(d);
- value.setFlags(flags);
- return new Token(NUMBER,
- text.toString(), value);
- }
- }
- }
-
- /* Either a decimal part, or a hex exponent. */
- private String _number_part(StringBuilder text, int base)
- throws IOException,
- LexerException {
- StringBuilder part = new StringBuilder();
- int d = read();
- while (Character.digit(d, base) != -1) {
- text.append((char)d);
- part.append((char)d);
- d = read();
- }
- unread(d);
- return part.toString();
- }
-
- /* We already chewed a zero, so empty is fine. */
- 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. */
- private Token number_hex(char x)
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder("0");
- text.append(x);
- String integer = _number_part(text, 16);
- NumericValue value = new NumericValue(16, integer);
- int d = read();
- if (d == '.') {
- String fraction = _number_part(text, 16);
- value.setFractionalPart(fraction);
- d = read();
- }
- if (d == 'P' || d == 'p') {
- String exponent = _number_part(text, 10);
- value.setExponent(exponent);
- d = read();
- }
- // XXX Make sure it's got enough parts
- return _number_suffix(text, value, d);
- }
-
- /* We know we have at least one valid digit, but empty is not
- * fine. */
- private Token number_decimal()
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder();
- String integer = _number_part(text, 10);
- NumericValue value = new NumericValue(10, integer);
- int d = read();
- if (d == '.') {
- String fraction = _number_part(text, 10);
- value.setFractionalPart(fraction);
- d = read();
- }
- if (d == 'E' || d == 'e') {
- String exponent = _number_part(text, 10);
- value.setExponent(exponent);
- d = read();
- }
- // XXX Make sure it's got enough parts
- return _number_suffix(text, value, d);
- }
-
- private Token identifier(int c)
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder();
- int d;
- text.append((char)c);
- for (;;) {
- d = read();
- if (Character.isIdentifierIgnorable(d))
- ;
- else if (Character.isJavaIdentifierPart(d))
- text.append((char)d);
- else
- break;
- }
- unread(d);
- return new Token(IDENTIFIER, text.toString());
- }
-
- private Token whitespace(int c)
- throws IOException,
- LexerException {
- StringBuilder text = new StringBuilder();
- int d;
- text.append((char)c);
- for (;;) {
- d = read();
- if (ppvalid && isLineSeparator(d)) /* XXX Ugly. */
- break;
- if (Character.isWhitespace(d))
- text.append((char)d);
- else
- break;
- }
- unread(d);
- return new Token(WHITESPACE, text.toString());
- }
-
- /* No token processed by cond() contains a newline. */
- private Token cond(char c, int yes, int no)
- throws IOException,
- LexerException {
- int d = read();
- if (c == d)
- return new Token(yes);
- unread(d);
- return new Token(no);
- }
-
- public Token token()
- throws IOException,
- LexerException {
- Token tok = null;
-
- int _l = line;
- int _c = column;
-
- int c = read();
- int d;
-
- switch (c) {
- case '\n':
- if (ppvalid) {
- bol = true;
- if (include) {
- tok = new Token(NL, _l, _c, "\n");
- }
- else {
- int nls = 0;
- do {
- nls++;
- d = read();
- } while (d == '\n');
- unread(d);
- char[] text = new char[nls];
- for (int i = 0; i < text.length; i++)
- text[i] = '\n';
- // Skip the bol = false below.
- tok = new Token(NL, _l, _c, new String(text));
- }
- if (DEBUG)
- System.out.println("lx: Returning NL: " + tok);
- return tok;
- }
- /* Let it be handled as whitespace. */
- break;
-
- case '!':
- tok = cond('=', NE, '!');
- break;
-
- case '#':
- if (bol)
- tok = new Token(HASH);
- else
- tok = cond('#', PASTE, '#');
- break;
-
- case '+':
- d = read();
- if (d == '+')
- tok = new Token(INC);
- else if (d == '=')
- tok = new Token(PLUS_EQ);
- else
- unread(d);
- break;
- case '-':
- d = read();
- if (d == '-')
- tok = new Token(DEC);
- else if (d == '=')
- tok = new Token(SUB_EQ);
- else if (d == '>')
- tok = new Token(ARROW);
- else
- unread(d);
- break;
-
- case '*':
- tok = cond('=', MULT_EQ, '*');
- break;
- case '/':
- d = read();
- if (d == '*')
- tok = ccomment();
- else if (d == '/')
- tok = cppcomment();
- else if (d == '=')
- tok = new Token(DIV_EQ);
- else
- unread(d);
- break;
-
- case '%':
- d = read();
- if (d == '=')
- tok = new Token(MOD_EQ);
- else if (digraphs && d == '>')
- tok = new Token('}'); // digraph
- else if (digraphs && d == ':') PASTE: {
- d = read();
- if (d != '%') {
- unread(d);
- tok = new Token('#'); // digraph
- break PASTE;
- }
- d = read();
- if (d != ':') {
- unread(d); // Unread 2 chars here.
- unread('%');
- tok = new Token('#'); // digraph
- break PASTE;
- }
- tok = new Token(PASTE); // digraph
- }
- else
- unread(d);
- break;
-
- case ':':
- /* :: */
- d = read();
- if (digraphs && d == '>')
- tok = new Token(']'); // digraph
- else
- unread(d);
- break;
-
- case '<':
- if (include) {
- tok = string('<', '>');
- }
- else {
- d = read();
- if (d == '=')
- tok = new Token(LE);
- else if (d == '<')
- tok = cond('=', LSH_EQ, LSH);
- else if (digraphs && d == ':')
- tok = new Token('['); // digraph
- else if (digraphs && d == '%')
- tok = new Token('{'); // digraph
- else
- unread(d);
- }
- break;
-
- case '=':
- tok = cond('=', EQ, '=');
- break;
-
- case '>':
- d = read();
- if (d == '=')
- tok = new Token(GE);
- else if (d == '>')
- tok = cond('=', RSH_EQ, RSH);
- else
- unread(d);
- break;
-
- case '^':
- tok = cond('=', XOR_EQ, '^');
- break;
-
- case '|':
- d = read();
- if (d == '=')
- tok = new Token(OR_EQ);
- else if (d == '|')
- tok = cond('=', LOR_EQ, LOR);
- else
- unread(d);
- break;
- case '&':
- d = read();
- if (d == '&')
- tok = cond('=', LAND_EQ, LAND);
- else if (d == '=')
- tok = new Token(AND_EQ);
- else
- unread(d);
- break;
-
- case '.':
- d = read();
- if (d == '.')
- tok = cond('.', ELLIPSIS, RANGE);
- else
- unread(d);
- if (Character.isDigit(d)) {
- unread('.');
- tok = number_decimal();
- }
- /* XXX decimal fraction */
- break;
-
- case '0':
- /* octal or hex */
- d = read();
- if (d == 'x' || d == 'X')
- tok = number_hex((char)d);
- else {
- unread(d);
- tok = number_octal();
- }
- break;
-
- case '\'':
- tok = string('\'', '\'');
- break;
-
- case '"':
- tok = string('"', '"');
- break;
-
- case -1:
- close();
- tok = new Token(EOF, _l, _c, "<eof>");
- break;
- }
-
- if (tok == null) {
- if (Character.isWhitespace(c)) {
- tok = whitespace(c);
- }
- else if (Character.isDigit(c)) {
- unread(c);
- tok = number_decimal();
- }
- else if (Character.isJavaIdentifierStart(c)) {
- tok = identifier(c);
- }
- else {
- tok = new Token(c);
- }
- }
-
- if (bol) {
- switch (tok.getType()) {
- case WHITESPACE:
- case CCOMMENT:
- break;
- default:
- bol = false;
- break;
- }
- }
-
- tok.setLocation(_l, _c);
- if (DEBUG)
- System.out.println("lx: Returning " + tok);
- // (new Exception("here")).printStackTrace(System.out);
- return tok;
- }
-
- public void close()
- throws IOException {
- if (reader != null) {
- reader.close();
- reader = null;
- }
- super.close();
- }
-
-}
diff --git a/src/java/org/anarres/cpp/MacroTokenSource.java b/src/java/org/anarres/cpp/MacroTokenSource.java
deleted file mode 100644
index 0759875..0000000
--- a/src/java/org/anarres/cpp/MacroTokenSource.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PushbackReader;
-import java.io.Reader;
-import java.io.StringReader;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-import static org.anarres.cpp.Token.*;
-
-/* This source should always be active, since we don't expand macros
- * in any inactive context. */
-/* pp */ class MacroTokenSource extends Source {
- private Macro macro;
- private Iterator<Token> tokens; /* Pointer into the macro. */
- private List<Argument> args; /* { unexpanded, expanded } */
- private Iterator<Token> arg; /* "current expansion" */
-
- /* pp */ MacroTokenSource(Macro m, List<Argument> args) {
- this.macro = m;
- this.tokens = m.getTokens().iterator();
- this.args = args;
- this.arg = null;
- }
-
- @Override
- /* pp */ boolean isExpanding(Macro m) {
- /* When we are expanding an arg, 'this' macro is not
- * being expanded, and thus we may re-expand it. */
- if (/* XXX this.arg == null && */ this.macro == m)
- return true;
- return super.isExpanding(m);
- }
-
- /* XXX Called from Preprocessor [ugly]. */
- /* pp */ static void escape(StringBuilder buf, CharSequence cs) {
- for (int i = 0; i < cs.length(); i++) {
- char c = cs.charAt(i);
- switch (c) {
- case '\\':
- buf.append("\\\\");
- break;
- case '"':
- buf.append("\\\"");
- break;
- case '\n':
- buf.append("\\n");
- break;
- case '\r':
- buf.append("\\r");
- break;
- default:
- buf.append(c);
- }
- }
- }
-
- private void concat(StringBuilder buf, Argument arg) {
- Iterator<Token> it = arg.iterator();
- while (it.hasNext()) {
- Token tok = it.next();
- buf.append(tok.getText());
- }
- }
-
- private Token stringify(Token pos, Argument arg) {
- StringBuilder buf = new StringBuilder();
- concat(buf, arg);
- // System.out.println("Concat: " + arg + " -> " + buf);
- StringBuilder str = new StringBuilder("\"");
- escape(str, buf);
- str.append("\"");
- // System.out.println("Escape: " + buf + " -> " + str);
- return new Token(STRING,
- pos.getLine(), pos.getColumn(),
- str.toString(), buf.toString());
- }
-
-
- /* At this point, we have consumed the first M_PASTE.
- * @see Macro#addPaste(Token) */
- private void paste(Token ptok)
- throws IOException,
- LexerException {
- StringBuilder buf = new StringBuilder();
- Token err = null;
- /* We know here that arg is null or expired,
- * since we cannot paste an expanded arg. */
-
- int count = 2;
- for (int i = 0; i < count; i++) {
- if (!tokens.hasNext()) {
- /* XXX This one really should throw. */
- error(ptok.getLine(), ptok.getColumn(),
- "Paste at end of expansion");
- buf.append(' ').append(ptok.getText());
- break;
- }
- Token tok = tokens.next();
- // System.out.println("Paste " + tok);
- switch (tok.getType()) {
- case M_PASTE:
- /* One extra to paste, plus one because the
- * paste token didn't count. */
- count += 2;
- ptok = tok;
- break;
- case M_ARG:
- int idx = ((Integer)tok.getValue()).intValue();
- concat(buf, args.get(idx));
- break;
- /* XXX Test this. */
- case CCOMMENT:
- case CPPCOMMENT:
- break;
- default:
- buf.append(tok.getText());
- break;
- }
- }
-
- /* Push and re-lex. */
- /*
- StringBuilder src = new StringBuilder();
- escape(src, buf);
- StringLexerSource sl = new StringLexerSource(src.toString());
- */
- StringLexerSource sl = new StringLexerSource(buf.toString());
-
- /* XXX Check that concatenation produces a valid token. */
-
- arg = new SourceIterator(sl);
- }
-
- public Token token()
- throws IOException,
- LexerException {
- for (;;) {
- /* Deal with lexed tokens first. */
-
- if (arg != null) {
- if (arg.hasNext()) {
- Token tok = arg.next();
- /* XXX PASTE -> INVALID. */
- assert tok.getType() != M_PASTE :
- "Unexpected paste token";
- return tok;
- }
- arg = null;
- }
-
- if (!tokens.hasNext())
- return new Token(EOF, -1, -1, ""); /* End of macro. */
- Token tok = tokens.next();
- int idx;
- switch (tok.getType()) {
- case M_STRING:
- /* Use the nonexpanded arg. */
- idx = ((Integer)tok.getValue()).intValue();
- return stringify(tok, args.get(idx));
- case M_ARG:
- /* Expand the arg. */
- idx = ((Integer)tok.getValue()).intValue();
- // System.out.println("Pushing arg " + args.get(idx));
- arg = args.get(idx).expansion();
- break;
- case M_PASTE:
- paste(tok);
- break;
- default:
- return tok;
- }
- } /* for */
- }
-
- public String toString() {
- StringBuilder buf = new StringBuilder();
- buf.append("expansion of ").append(macro.getName());
- Source parent = getParent();
- if (parent != null)
- buf.append(" in ").append(String.valueOf(parent));
- return buf.toString();
- }
-}
diff --git a/src/java/org/anarres/cpp/Main.java b/src/java/org/anarres/cpp/Main.java
deleted file mode 100644
index 8777c72..0000000
--- a/src/java/org/anarres/cpp/Main.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-
-import gnu.getopt.Getopt;
-import gnu.getopt.LongOpt;
-
-import static org.anarres.cpp.Token.*;
-
-/**
- * (Currently a simple test class).
- */
-public class Main {
-
- private static class Option extends LongOpt {
- private String eg;
- private String help;
- public Option(String word, int arg, int ch,
- String eg, String help) {
- super(word, arg, null, ch);
- this.eg = eg;
- this.help = help;
- }
- }
-
- private static final Option[] OPTS = new Option[] {
- new Option("help", LongOpt.NO_ARGUMENT, 'h', null,
- "Displays help and usage information."),
- new Option("define", LongOpt.REQUIRED_ARGUMENT, 'D', "name=definition",
- "Defines the given macro."),
- new Option("undefine", LongOpt.REQUIRED_ARGUMENT, 'U', "name",
- "Undefines the given macro, previously either builtin or defined using -D."),
- new Option("include", LongOpt.REQUIRED_ARGUMENT, 1, "file",
- "Process file as if \"#" + "include \"file\"\" appeared as the first line of the primary source file."),
- new Option("incdir", LongOpt.REQUIRED_ARGUMENT, 'I', "dir",
- "Adds the directory dir to the list of directories to be searched for header files."),
- new Option("iquote", LongOpt.REQUIRED_ARGUMENT, 0, "dir",
- "Adds the directory dir to the list of directories to be searched for header files included using \"\"."),
- new Option("warning", LongOpt.REQUIRED_ARGUMENT, 'W', "type",
- "Enables the named warning class (" + getWarnings() + ")."),
- new Option("no-warnings", LongOpt.NO_ARGUMENT, 'w', null,
- "Disables ALL warnings."),
- new Option("verbose", LongOpt.NO_ARGUMENT, 'v', null,
- "Operates incredibly verbosely."),
- new Option("debug", LongOpt.NO_ARGUMENT, 3, null,
- "Operates incredibly verbosely."),
- new Option("version", LongOpt.NO_ARGUMENT, 2, null,
- "Prints jcpp's version number (" + Version.getVersion() + ")"),
- };
-
- private static CharSequence getWarnings() {
- StringBuilder buf = new StringBuilder();
- for (Warning w : Warning.values()) {
- if (buf.length() > 0)
- buf.append(", ");
- String name = w.name().toLowerCase();
- buf.append(name.replace('_', '-'));
- }
- return buf;
- }
-
- public static void main(String[] args) throws Exception {
- (new Main()).run(args);
- }
-
- public void run(String[] args) throws Exception {
- Option[]opts = OPTS;
- String sopts = getShortOpts(opts);
- Getopt g = new Getopt("jcpp", args, sopts, opts);
- int c;
- String arg;
- int idx;
-
- Preprocessor pp = new Preprocessor();
- pp.addFeature(Feature.DIGRAPHS);
- pp.addFeature(Feature.TRIGRAPHS);
- pp.addFeature(Feature.LINEMARKERS);
- pp.addWarning(Warning.IMPORT);
- pp.setListener(new PreprocessorListener());
- pp.addMacro("__JCPP__");
- pp.getSystemIncludePath().add("/usr/local/include");
- pp.getSystemIncludePath().add("/usr/include");
- pp.getFrameworksPath().add("/System/Library/Frameworks");
- pp.getFrameworksPath().add("/Library/Frameworks");
- pp.getFrameworksPath().add("/Local/Library/Frameworks");
-
- GETOPT: while ((c = g.getopt()) != -1) {
- switch (c) {
- case 'D':
- arg = g.getOptarg();
- idx = arg.indexOf('=');
- if (idx == -1)
- pp.addMacro(arg);
- else
- pp.addMacro(arg.substring(0, idx),
- arg.substring(idx + 1));
- break;
- case 'U':
- pp.getMacros().remove(g.getOptarg());
- break;
- case 'I':
- pp.getSystemIncludePath().add(g.getOptarg());
- break;
- case 0: // --iquote=
- pp.getQuoteIncludePath().add(g.getOptarg());
- break;
- case 'W':
- arg = g.getOptarg().toUpperCase();
- arg = arg.replace('-', '_');
- if (arg.equals("ALL"))
- pp.addWarnings(EnumSet.allOf(Warning.class));
- else
- pp.addWarning(Enum.valueOf(Warning.class, arg));
- break;
- case 'w':
- pp.getWarnings().clear();
- break;
- case 1: // --include=
- // pp.addInput(new File(g.getOptarg()));
- // Comply exactly with spec.
- pp.addInput(new StringLexerSource(
- "#" + "include \"" + g.getOptarg() + "\"\n"
- ));
- break;
- case 2: // --version
- version(System.out);
- return;
- case 'v':
- pp.addFeature(Feature.VERBOSE);
- break;
- case 3:
- pp.addFeature(Feature.DEBUG);
- break;
- case 'h':
- usage(getClass().getName(), opts);
- return;
- default:
- throw new Exception("Illegal option " + (char)c);
- case '?':
- continue; /* Make failure-proof. */
- }
- }
-
- for (int i = g.getOptind(); i < args.length; i++)
- pp.addInput(new FileLexerSource(new File(args[i])));
- if (g.getOptind() == args.length)
- pp.addInput(new InputLexerSource(System.in));
-
- if (pp.getFeature(Feature.VERBOSE)) {
- System.err.println("#"+"include \"...\" search starts here:");
- for (String dir : pp.getQuoteIncludePath())
- System.err.println(" " + dir);
- System.err.println("#"+"include <...> search starts here:");
- for (String dir : pp.getSystemIncludePath())
- System.err.println(" " + dir);
- System.err.println("End of search list.");
- }
-
- try {
- for (;;) {
- Token tok = pp.token();
- if (tok == null)
- break;
- if (tok.getType() == Token.EOF)
- break;
- System.out.print(tok.getText());
- }
- }
- catch (Exception e) {
- e.printStackTrace(System.err);
- Source s = pp.getSource();
- while (s != null) {
- System.err.println(" -> " + s);
- s = s.getParent();
- }
- }
-
- }
-
- private void version(PrintStream out) {
- out.println("Anarres Java C Preprocessor version " + Version.getVersion());
- out.println("Copyright (C) 2008 Shevek (http://www.anarres.org/).");
- out.println("This is free software; see the source for copying conditions. There is NO");
- out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
- }
-
-
- private static String getShortOpts(Option[] opts)
- throws Exception {
- StringBuilder buf = new StringBuilder();
- for (int i = 0; i < opts.length; i++) {
- char c = (char)opts[i].getVal();
- if (!Character.isLetterOrDigit(c))
- continue;
- for (int j = 0; j < buf.length(); j++)
- if (buf.charAt(j) == c)
- throw new Exception(
- "Duplicate short option " + c
- );
- buf.append(c);
- switch (opts[i].getHasArg()) {
- case LongOpt.NO_ARGUMENT:
- break;
- case LongOpt.OPTIONAL_ARGUMENT:
- buf.append("::");
- break;
- case LongOpt.REQUIRED_ARGUMENT:
- buf.append(":");
- break;
- }
- }
- return buf.toString();
- }
-
- /* This is incomplete but nearly there. */
- /**
- * Wraps a string.
- *
- * The output form is:
- * <pre>
- * prefix in[0]
- * &lt;--indent-&gt; in[1]
- * &lt;--indent-&gt; in[2]
- * &lt;-----width----&gt;
- * </pre>
- */
- /* XXX There's some of this in commons. */
- private static String wrap(String in, String prefix,
- int indent, int width) {
- StringBuilder buf = new StringBuilder(prefix);
-
- while (buf.length() < indent)
- buf.append(' ');
-
- int start = 0;
-
- while (start < in.length()) {
- while (start < in.length() &&
- Character.isWhitespace(in.charAt(start)))
- start++;
-
- int end = start + width - indent;
-
- if (end > in.length()) {
- buf.append(in.substring(start));
- break;
- }
-
- int idx = end;
- while (!Character.isWhitespace(in.charAt(idx)))
- idx--;
-
- if (idx == start) {
- idx = end - 1;
- buf.append(in.substring(start, idx));
- buf.append('-');
- }
- else {
- buf.append(in.substring(start, idx));
- start = idx;
- }
-
- start = idx;
- }
-
- return buf.toString();
- }
-
- private static void usage(String command, Option[] options) {
- StringBuilder text = new StringBuilder("Usage: ");
- text.append(command).append('\n');
- for (int i = 0; i < options.length; i++) {
- StringBuilder line = new StringBuilder();
- Option opt = options[i];
- line.append(" --").append(opt.getName());
- switch (opt.getHasArg()) {
- case LongOpt.NO_ARGUMENT:
- break;
- case LongOpt.OPTIONAL_ARGUMENT:
- line.append("[=").append(opt.eg).append(']');
- break;
- case LongOpt.REQUIRED_ARGUMENT:
- line.append('=').append(opt.eg);
- break;
- }
- if (Character.isLetterOrDigit(opt.getVal()))
- line.append(" (-").append((char)opt.getVal()).append(")");
- if (line.length() < 30) {
- while (line.length() < 30)
- line.append(' ');
- }
- else {
- line.append('\n');
- for (int j = 0; j < 30; j++)
- line.append(' ');
- }
- /* This should use wrap. */
- line.append(opt.help);
- line.append('\n');
- text.append(line);
- }
-
- System.out.println(text);
- }
-
-
-
-#if (false)
- public static void oldmain(String[] args) throws Exception {
- List<String> path = new ArrayList<String>();
- path.add("/usr/include");
- path.add("/usr/local/include");
- path.add("/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include");
-
- Source source = new FileLexerSource(new File(args[0]));
- Preprocessor pp = new Preprocessor(source);
- pp.setSystemIncludePath(path);
-
- for (int i = 1; i < args.length; i++) {
- pp.push_source(new FileLexerSource(new File(args[i])),true);
- }
-
- Macro m = new Macro("__WORDSIZE");
- m.addToken(new Token(NUMBER, -1, -1, "32", new NumericValue(10, "32")));
- pp.addMacro(m);
-
- m = new Macro("__STDC__");
- m.addToken(new Token(NUMBER, -1, -1, "1", new NumericValue(10, "1")));
- pp.addMacro(m);
-
- try {
- for (;;) {
- Token tok = pp.token();
- if (tok != null && tok.getType() == Token.EOF)
- break;
- switch (2) {
- case 0:
- System.out.print(tok);
- break;
- case 1:
- System.out.print("[" + tok.getText() + "]");
- break;
- case 2:
- System.out.print(tok.getText());
- break;
- }
- }
- }
- catch (Exception e) {
- e.printStackTrace();
- Source s = pp.getSource();
- while (s != null) {
- System.out.println(" -> " + s);
- s = s.getParent();
- }
-
- /*
- Iterator<State> it = pp.states.iterator();
- while (it.hasNext()) {
- System.out.println(" -? " + it.next());
- }
- */
-
- }
-
- Map<String,Macro> macros = pp.getMacros();
- List<String> keys = new ArrayList<String>(
- macros.keySet()
- );
- Collections.sort(keys);
- Iterator<String> mt = keys.iterator();
- while (mt.hasNext()) {
- String key = mt.next();
- Macro macro = macros.get(key);
- System.out.println("#" + "macro " + macro);
- }
-
- }
-#end
-
-}
diff --git a/src/java/org/anarres/cpp/NumericValue.java b/src/java/org/anarres/cpp/NumericValue.java
deleted file mode 100644
index f8b1559..0000000
--- a/src/java/org/anarres/cpp/NumericValue.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-public class NumericValue extends Number {
- public static final int F_UNSIGNED = 1;
- public static final int F_INT = 2;
- public static final int F_LONG = 4;
- public static final int F_LONGLONG = 8;
- public static final int F_FLOAT = 16;
- public static final int F_DOUBLE = 32;
-
- public static final int FF_SIZE = F_INT | F_LONG | F_LONGLONG | F_FLOAT | F_DOUBLE;
-
- private int base;
- private String integer;
- private String fraction;
- private String exponent;
- private int flags;
-
- public NumericValue(int base, String integer) {
- this.base = base;
- this.integer = integer;
- }
-
- public int getBase() {
- return base;
- }
-
- public String getIntegerPart() {
- return integer;
- }
-
- public String getFractionalPart() {
- return fraction;
- }
-
- /* pp */ void setFractionalPart(String fraction) {
- this.fraction = fraction;
- }
-
- public String getExponent() {
- return exponent;
- }
-
- /* pp */ void setExponent(String exponent) {
- this.exponent = exponent;
- }
-
- public int getFlags() {
- return flags;
- }
-
- /* pp */ void setFlags(int flags) {
- this.flags = flags;
- }
-
- /**
- * So, it turns out that parsing arbitrary bases into arbitrary
- * precision numbers is nontrivial, and this routine gets it wrong
- * in many important cases.
- */
- public BigDecimal toBigDecimal() {
- int scale = 0;
- String text = getIntegerPart();
- String t_fraction = getFractionalPart();
- if (t_fraction != null) {
- text += getFractionalPart();
- // XXX Wrong for anything but base 10.
- scale += getFractionalPart().length();
- }
- if (getExponent() != null)
- scale -= Integer.parseInt(getExponent());
- BigInteger unscaled = new BigInteger(text, getBase());
- return new BigDecimal(unscaled, scale);
- }
-
- public Number toJavaLangNumber() {
- int flags = getFlags();
- if ((flags & F_DOUBLE) != 0)
- return doubleValue();
- else if ((flags & F_FLOAT) != 0)
- return floatValue();
- else if ((flags & (F_LONG | F_LONGLONG)) != 0)
- return longValue();
- else if ((flags & F_INT) != 0)
- return intValue();
- else if (getFractionalPart() != null)
- return doubleValue(); // .1 is a double in Java.
- else if (getExponent() != null)
- return doubleValue();
- else
- return intValue();
- }
-
- @Override
- public int intValue() {
- return Integer.parseInt(toString());
- }
-
- @Override
- public long longValue() {
- return Long.parseLong(toString());
- }
-
- @Override
- public float floatValue() {
- return Float.parseFloat(toString());
- }
-
- @Override
- public double doubleValue() {
- return Double.parseDouble(toString());
- }
-
- private boolean appendFlags(StringBuilder buf, String suffix, int flag) {
- if ((getFlags() & flag) != flag)
- return false;
- buf.append(suffix);
- return true;
- }
-
- @Override
- public String toString() {
- StringBuilder buf = new StringBuilder();
- switch (base) {
- case 8:
- buf.append('0');
- break;
- case 10:
- break;
- case 16:
- buf.append("0x");
- break;
- case 2:
- buf.append('b');
- break;
- default:
- buf.append("[base-").append(base).append("]");
- break;
- }
- buf.append(getIntegerPart());
- if (getFractionalPart() != null)
- buf.append('.').append(getFractionalPart());
- if (getExponent() != null) {
- buf.append(base > 10 ? 'p' : 'e');
- buf.append(getExponent());
- }
- /*
- if (appendFlags(buf, "ui", F_UNSIGNED | F_INT));
- else if (appendFlags(buf, "ul", F_UNSIGNED | F_LONG));
- else if (appendFlags(buf, "ull", F_UNSIGNED | F_LONGLONG));
- else if (appendFlags(buf, "i", F_INT));
- else if (appendFlags(buf, "l", F_LONG));
- else if (appendFlags(buf, "ll", F_LONGLONG));
- else if (appendFlags(buf, "f", F_FLOAT));
- else if (appendFlags(buf, "d", F_DOUBLE));
- */
- return buf.toString();
- }
-}
diff --git a/src/java/org/anarres/cpp/Preprocessor.java b/src/java/org/anarres/cpp/Preprocessor.java
deleted file mode 100644
index 9bda523..0000000
--- a/src/java/org/anarres/cpp/Preprocessor.java
+++ /dev/null
@@ -1,1983 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-
-import static org.anarres.cpp.Token.*;
-
-/**
- * A C Preprocessor.
- * The Preprocessor outputs a token stream which does not need
- * re-lexing for C or C++. Alternatively, the output text may be
- * reconstructed by concatenating the {@link Token#getText() text}
- * values of the returned {@link Token Tokens}. (See
- * {@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.
-*/
-
-public class Preprocessor implements Closeable {
- private static final Source INTERNAL = new Source() {
- @Override
- public Token token()
- throws IOException,
- LexerException {
- throw new LexerException("Cannot read from " + getName());
- }
- @Override
- public String getPath() {
- return "<internal-data>";
- }
- @Override
- public String getName() {
- return "internal data";
- }
- };
- private static final Macro __LINE__ = new Macro(INTERNAL, "__LINE__");
- private static final Macro __FILE__ = new Macro(INTERNAL, "__FILE__");
- private static final Macro __COUNTER__ = new Macro(INTERNAL, "__COUNTER__");
-
- private List<Source> inputs;
-
- /* The fundamental engine. */
- private Map<String,Macro> macros;
- private Stack<State> states;
- private Source source;
-
- /* Miscellaneous support. */
- private int counter;
-
- /* Support junk to make it work like cpp */
- private List<String> quoteincludepath; /* -iquote */
- private List<String> sysincludepath; /* -I */
- private List<String> frameworkspath;
- private Set<Feature> features;
- private Set<Warning> warnings;
- private VirtualFileSystem filesystem;
- private PreprocessorListener listener;
-
- public Preprocessor() {
- this.inputs = new ArrayList<Source>();
-
- this.macros = new HashMap<String,Macro>();
- macros.put(__LINE__.getName(), __LINE__);
- macros.put(__FILE__.getName(), __FILE__);
- macros.put(__COUNTER__.getName(), __COUNTER__);
- this.states = new Stack<State>();
- states.push(new State());
- this.source = null;
-
- this.counter = 0;
-
- this.quoteincludepath = new ArrayList<String>();
- this.sysincludepath = new ArrayList<String>();
- this.frameworkspath = new ArrayList<String>();
- this.features = EnumSet.noneOf(Feature.class);
- this.warnings = EnumSet.noneOf(Warning.class);
- this.filesystem = new JavaFileSystem();
- this.listener = null;
- }
-
- public Preprocessor(Source initial) {
- this();
- addInput(initial);
- }
-
- /** Equivalent to
- * 'new Preprocessor(new {@link FileLexerSource}(file))'
- */
- public Preprocessor(File file)
- throws IOException {
- this(new FileLexerSource(file));
- }
-
- /**
- * Sets the VirtualFileSystem used by this Preprocessor.
- */
- public void setFileSystem(VirtualFileSystem filesystem) {
- this.filesystem = filesystem;
- }
-
- /**
- * Returns the VirtualFileSystem used by this Preprocessor.
- */
- public VirtualFileSystem getFileSystem() {
- return filesystem;
- }
-
- /**
- * Sets the PreprocessorListener which handles events for
- * this Preprocessor.
- *
- * The listener is notified of warnings, errors and source
- * changes, amongst other things.
- */
- public void setListener(PreprocessorListener listener) {
- this.listener = listener;
- Source s = source;
- while (s != null) {
- // s.setListener(listener);
- s.init(this);
- s = s.getParent();
- }
- }
-
- /**
- * Returns the PreprocessorListener which handles events for
- * this Preprocessor.
- */
- public PreprocessorListener getListener() {
- return listener;
- }
-
- /**
- * Returns the feature-set for this Preprocessor.
- *
- * This set may be freely modified by user code.
- */
- public Set<Feature> getFeatures() {
- return features;
- }
-
- /**
- * Adds a feature to the feature-set of this Preprocessor.
- */
- public void addFeature(Feature f) {
- features.add(f);
- }
-
- /**
- * Adds features to the feature-set of this Preprocessor.
- */
- public void addFeatures(Collection<Feature> f) {
- features.addAll(f);
- }
-
- /**
- * Returns true if the given feature is in
- * the feature-set of this Preprocessor.
- */
- public boolean getFeature(Feature f) {
- return features.contains(f);
- }
-
- /**
- * Returns the warning-set for this Preprocessor.
- *
- * This set may be freely modified by user code.
- */
- public Set<Warning> getWarnings() {
- return warnings;
- }
-
- /**
- * Adds a warning to the warning-set of this Preprocessor.
- */
- public void addWarning(Warning w) {
- warnings.add(w);
- }
-
- /**
- * Adds warnings to the warning-set of this Preprocessor.
- */
- public void addWarnings(Collection<Warning> w) {
- warnings.addAll(w);
- }
-
- /**
- * Returns true if the given warning is in
- * the warning-set of this Preprocessor.
- */
- public boolean getWarning(Warning w) {
- return warnings.contains(w);
- }
-
- /**
- * Adds input for the Preprocessor.
- *
- * Inputs are processed in the order in which they are added.
- */
- public void addInput(Source source) {
- source.init(this);
- inputs.add(source);
- }
-
- /**
- * Adds input for the Preprocessor.
- *
- * @see #addInput(Source)
- */
- public void addInput(File file)
- throws IOException {
- addInput(new FileLexerSource(file));
- }
-
-
- /**
- * Handles an error.
- *
- * If a PreprocessorListener is installed, it receives the
- * error. Otherwise, an exception is thrown.
- */
- protected void error(int line, int column, String msg)
- throws LexerException {
- if (listener != null)
- listener.handleError(source, line, column, msg);
- else
- throw new LexerException("Error at " + line + ":" + column + ": " + msg);
- }
-
- /**
- * Handles an error.
- *
- * If a PreprocessorListener is installed, it receives the
- * error. Otherwise, an exception is thrown.
- *
- * @see #error(int, int, String)
- */
- protected void error(Token tok, String msg)
- throws LexerException {
- error(tok.getLine(), tok.getColumn(), msg);
- }
-
- /**
- * Handles a warning.
- *
- * If a PreprocessorListener is installed, it receives the
- * warning. Otherwise, an exception is thrown.
- */
- protected void warning(int line, int column, String msg)
- throws LexerException {
- if (warnings.contains(Warning.ERROR))
- error(line, column, msg);
- else if (listener != null)
- listener.handleWarning(source, line, column, msg);
- else
- throw new LexerException("Warning at " + line + ":" + column + ": " + msg);
- }
-
- /**
- * Handles a warning.
- *
- * If a PreprocessorListener is installed, it receives the
- * warning. Otherwise, an exception is thrown.
- *
- * @see #warning(int, int, String)
- */
- protected void warning(Token tok, String msg)
- throws LexerException {
- warning(tok.getLine(), tok.getColumn(), msg);
- }
-
- /**
- * Adds a Macro to this Preprocessor.
- *
- * The given {@link Macro} object encapsulates both the name
- * and the expansion.
- */
- public void addMacro(Macro m) throws LexerException {
- // System.out.println("Macro " + m);
- String name = m.getName();
- /* Already handled as a source error in macro(). */
- if ("defined".equals(name))
- throw new LexerException("Cannot redefine name 'defined'");
- macros.put(m.getName(), m);
- }
-
- /**
- * Defines the given name as a macro.
- *
- * The String value is lexed into a token stream, which is
- * used as the macro expansion.
- */
- public void addMacro(String name, String value)
- throws LexerException {
- try {
- Macro m = new Macro(name);
- StringLexerSource s = new StringLexerSource(value);
- for (;;) {
- Token tok = s.token();
- if (tok.getType() == EOF)
- break;
- m.addToken(tok);
- }
- addMacro(m);
- }
- catch (IOException e) {
- throw new LexerException(e);
- }
- }
-
- /**
- * Defines the given name as a macro, with the value <code>1</code>.
- *
- * This is a convnience method, and is equivalent to
- * <code>addMacro(name, "1")</code>.
- */
- public void addMacro(String name)
- throws LexerException {
- addMacro(name, "1");
- }
-
- /**
- * Sets the user include path used by this Preprocessor.
- */
- /* Note for future: Create an IncludeHandler? */
- public void setQuoteIncludePath(List<String> path) {
- this.quoteincludepath = path;
- }
-
- /**
- * Returns the user include-path of this Preprocessor.
- *
- * This list may be freely modified by user code.
- */
- public List<String> getQuoteIncludePath() {
- return quoteincludepath;
- }
-
- /**
- * Sets the system include path used by this Preprocessor.
- */
- /* Note for future: Create an IncludeHandler? */
- public void setSystemIncludePath(List<String> path) {
- this.sysincludepath = path;
- }
-
- /**
- * Returns the system include-path of this Preprocessor.
- *
- * This list may be freely modified by user code.
- */
- public List<String> getSystemIncludePath() {
- return sysincludepath;
- }
-
- /**
- * Sets the Objective-C frameworks path used by this Preprocessor.
- */
- /* Note for future: Create an IncludeHandler? */
- public void setFrameworksPath(List<String> path) {
- this.frameworkspath = path;
- }
-
- /**
- * Returns the Objective-C frameworks path used by this
- * Preprocessor.
- *
- * This list may be freely modified by user code.
- */
- public List<String> getFrameworksPath() {
- return frameworkspath;
- }
-
- /**
- * Returns the Map of Macros parsed during the run of this
- * Preprocessor.
- */
- public Map<String,Macro> getMacros() {
- return macros;
- }
-
- /**
- * Returns the named macro.
- *
- * While you can modify the returned object, unexpected things
- * might happen if you do.
- */
- public Macro getMacro(String name) {
- return macros.get(name);
- }
-
-/* States */
-
- private void push_state() {
- State top = states.peek();
- states.push(new State(top));
- }
-
- private void pop_state()
- throws LexerException {
- State s = states.pop();
- if (states.isEmpty()) {
- error(0, 0, "#" + "endif without #" + "if");
- states.push(s);
- }
- }
-
- private boolean isActive() {
- State state = states.peek();
- return state.isParentActive() && state.isActive();
- }
-
-
-/* Sources */
-
- /**
- * Returns the top Source on the input stack.
- *
- * @see Source
- * @see #push_source(Source,boolean)
- * @see #pop_source()
- */
- protected Source getSource() {
- return source;
- }
-
- /**
- * Pushes a Source onto the input stack.
- *
- * @see #getSource()
- * @see #pop_source()
- */
- protected void push_source(Source source, boolean autopop) {
- source.init(this);
- source.setParent(this.source, autopop);
- // source.setListener(listener);
- if (listener != null)
- listener.handleSourceChange(this.source, "suspend");
- this.source = source;
- if (listener != null)
- listener.handleSourceChange(this.source, "push");
- }
-
- /**
- * Pops a Source from the input stack.
- *
- * @see #getSource()
- * @see #push_source(Source,boolean)
- */
- protected void pop_source()
- throws IOException {
- if (listener != null)
- listener.handleSourceChange(this.source, "pop");
- Source s = this.source;
- this.source = s.getParent();
- /* Always a noop unless called externally. */
- s.close();
- if (listener != null && this.source != null)
- listener.handleSourceChange(this.source, "resume");
- }
-
-
-/* Source tokens */
-
- private Token source_token;
-
- /* XXX Make this include the NL, and make all cpp directives eat
- * their own NL. */
- private Token line_token(int line, String name, String extra) {
- StringBuilder buf = new StringBuilder();
- buf.append("#line ").append(line)
- .append(" \"");
- /* XXX This call to escape(name) is correct but ugly. */
- MacroTokenSource.escape(buf, name);
- buf.append("\"").append(extra).append("\n");
- return new Token(P_LINE, line, 0, buf.toString(), null);
- }
-
- private Token source_token()
- throws IOException,
- LexerException {
- if (source_token != null) {
- Token tok = source_token;
- source_token = null;
- if (getFeature(Feature.DEBUG))
- System.err.println("Returning unget token " + tok);
- return tok;
- }
-
- for (;;) {
- Source s = getSource();
- if (s == null) {
- if (inputs.isEmpty())
- return new Token(EOF);
- Source t = inputs.remove(0);
- push_source(t, true);
- if (getFeature(Feature.LINEMARKERS))
- return line_token(t.getLine(), t.getName(), " 1");
- continue;
- }
- Token tok = s.token();
- /* XXX Refactor with skipline() */
- if (tok.getType() == EOF && s.isAutopop()) {
- // System.out.println("Autopop " + s);
- pop_source();
- Source t = getSource();
- if (getFeature(Feature.LINEMARKERS)
- && s.isNumbered()
- && t != null) {
- /* We actually want 'did the nested source
- * contain a newline token', which isNumbered()
- * approximates. This is not perfect, but works. */
- return line_token(t.getLine() + 1, t.getName(), " 2");
- }
- continue;
- }
- if (getFeature(Feature.DEBUG))
- System.err.println("Returning fresh token " + tok);
- return tok;
- }
- }
-
- private void source_untoken(Token tok) {
- if (this.source_token != null)
- throw new IllegalStateException("Cannot return two tokens");
- this.source_token = tok;
- }
-
- private boolean isWhite(Token tok) {
- int type = tok.getType();
- return (type == WHITESPACE)
- || (type == CCOMMENT)
- || (type == CPPCOMMENT);
- }
-
- private Token source_token_nonwhite()
- throws IOException,
- LexerException {
- Token tok;
- do {
- tok = source_token();
- } while (isWhite(tok));
- return tok;
- }
-
- /**
- * Returns an NL or an EOF token.
- *
- * The metadata on the token will be correct, which is better
- * than generating a new one.
- *
- * This method can, as of recent patches, return a P_LINE token.
- */
- private Token source_skipline(boolean white)
- throws IOException,
- LexerException {
- // (new Exception("skipping line")).printStackTrace(System.out);
- Source s = getSource();
- Token tok = s.skipline(white);
- /* XXX Refactor with source_token() */
- if (tok.getType() == EOF && s.isAutopop()) {
- // System.out.println("Autopop " + s);
- pop_source();
- Source t = getSource();
- if (getFeature(Feature.LINEMARKERS)
- && s.isNumbered()
- && t != null) {
- /* We actually want 'did the nested source
- * contain a newline token', which isNumbered()
- * approximates. This is not perfect, but works. */
- return line_token(t.getLine() + 1, t.getName(), " 2");
- }
- }
- return tok;
- }
-
- /* processes and expands a macro. */
- private boolean macro(Macro m, Token orig)
- throws IOException,
- LexerException {
- Token tok;
- List<Argument> args;
-
- // System.out.println("pp: expanding " + m);
-
- if (m.isFunctionLike()) {
- OPEN: for (;;) {
- tok = source_token();
- // System.out.println("pp: open: token is " + tok);
- switch (tok.getType()) {
- case WHITESPACE: /* XXX Really? */
- case CCOMMENT:
- case CPPCOMMENT:
- case NL:
- break; /* continue */
- case '(':
- break OPEN;
- default:
- source_untoken(tok);
- return false;
- }
- }
-
- // tok = expanded_token_nonwhite();
- tok = source_token_nonwhite();
-
- /* We either have, or we should have args.
- * This deals elegantly with the case that we have
- * one empty arg. */
- if (tok.getType() != ')' || m.getArgs() > 0) {
- args = new ArrayList<Argument>();
-
- Argument arg = new Argument();
- int depth = 0;
- boolean space = false;
-
- ARGS: for (;;) {
- // System.out.println("pp: arg: token is " + tok);
- switch (tok.getType()) {
- case EOF:
- error(tok, "EOF in macro args");
- return false;
-
- case ',':
- if (depth == 0) {
- if (m.isVariadic() &&
- /* We are building the last arg. */
- args.size() == m.getArgs() - 1) {
- /* Just add the comma. */
- arg.addToken(tok);
- }
- else {
- args.add(arg);
- arg = new Argument();
- }
- }
- else {
- arg.addToken(tok);
- }
- space = false;
- break;
- case ')':
- if (depth == 0) {
- args.add(arg);
- break ARGS;
- }
- else {
- depth--;
- arg.addToken(tok);
- }
- space = false;
- break;
- case '(':
- depth++;
- arg.addToken(tok);
- space = false;
- break;
-
- case WHITESPACE:
- case CCOMMENT:
- case CPPCOMMENT:
- /* Avoid duplicating spaces. */
- space = true;
- break;
-
- default:
- /* Do not put space on the beginning of
- * an argument token. */
- if (space && ! arg.isEmpty())
- arg.addToken(Token.space);
- arg.addToken(tok);
- space = false;
- break;
-
- }
- // tok = expanded_token();
- tok = source_token();
- }
- /* space may still be true here, thus trailing space
- * is stripped from arguments. */
-
- if (args.size() != m.getArgs()) {
- error(tok,
- "macro " + m.getName() +
- " has " + m.getArgs() + " parameters " +
- "but given " + args.size() + " args");
- /* We could replay the arg tokens, but I
- * note that GNU cpp does exactly what we do,
- * i.e. output the macro name and chew the args.
- */
- return false;
- }
-
- /*
- for (Argument a : args)
- a.expand(this);
- */
-
- for (int i = 0; i < args.size(); i++) {
- args.get(i).expand(this);
- }
-
- // System.out.println("Macro " + m + " args " + args);
- }
- else {
- /* nargs == 0 and we (correctly) got () */
- args = null;
- }
-
- }
- else {
- /* Macro without args. */
- args = null;
- }
-
- 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())) }
- ), true);
- }
- else if (m == __FILE__) {
- StringBuilder buf = new StringBuilder("\"");
- String name = getSource().getName();
- if (name == null)
- name = "<no file>";
- for (int i = 0; i < name.length(); i++) {
- char c = name.charAt(i);
- switch (c) {
- case '\\':
- buf.append("\\\\");
- break;
- case '"':
- buf.append("\\\"");
- break;
- default:
- buf.append(c);
- break;
- }
- }
- buf.append("\"");
- String text = buf.toString();
- push_source(new FixedTokenSource(
- new Token[] { new Token(STRING,
- orig.getLine(), orig.getColumn(),
- text, text) }
- ), true);
- }
- else if (m == __COUNTER__) {
- /* This could equivalently have been done by adding
- * a special Macro subclass which overrides getTokens(). */
- int value = this.counter++;
- push_source(new FixedTokenSource(
- new Token[] { new Token(NUMBER,
- orig.getLine(), orig.getColumn(),
- String.valueOf(value),
- new NumericValue(10, "" + value)) }
- ), true);
- }
- else {
- push_source(new MacroTokenSource(m, args), true);
- }
-
- return true;
- }
-
- /**
- * Expands an argument.
- */
- /* I'd rather this were done lazily, but doing so breaks spec. */
- /* pp */ List<Token> expand(List<Token> arg)
- throws IOException,
- LexerException {
- List<Token> expansion = new ArrayList<Token>();
- boolean space = false;
-
- push_source(new FixedTokenSource(arg), false);
-
- EXPANSION: for (;;) {
- Token tok = expanded_token();
- switch (tok.getType()) {
- case EOF:
- break EXPANSION;
-
- case WHITESPACE:
- case CCOMMENT:
- case CPPCOMMENT:
- space = true;
- break;
-
- default:
- if (space && ! expansion.isEmpty())
- expansion.add(Token.space);
- expansion.add(tok);
- space = false;
- break;
- }
- }
-
- pop_source();
-
- return expansion;
- }
-
- /* processes a #define directive */
- private Token define()
- throws IOException,
- LexerException {
- Token tok = source_token_nonwhite();
- if (tok.getType() != IDENTIFIER) {
- error(tok, "Expected identifier");
- return source_skipline(false);
- }
- /* if predefined */
-
- String name = tok.getText();
- if ("defined".equals(name)) {
- error(tok, "Cannot redefine name 'defined'");
- return source_skipline(false);
- }
-
- Macro m = new Macro(getSource(), name);
- List<String> args;
-
- tok = source_token();
- if (tok.getType() == '(') {
- tok = source_token_nonwhite();
- if (tok.getType() != ')') {
- args = new ArrayList<String>();
- ARGS: for (;;) {
- switch (tok.getType()) {
- case IDENTIFIER:
- args.add(tok.getText());
- break;
- case NL:
- case EOF:
- error(tok,
- "Unterminated macro parameter list");
- return tok;
- default:
- error(tok,
- "error in macro parameters: " +
- tok.getText());
- return source_skipline(false);
- }
- tok = source_token_nonwhite();
- switch (tok.getType()) {
- case ',':
- break;
- case ELLIPSIS:
- tok = source_token_nonwhite();
- if (tok.getType() != ')')
- error(tok,
- "ellipsis must be on last argument");
- m.setVariadic(true);
- break ARGS;
- case ')':
- break ARGS;
-
- case NL:
- case EOF:
- /* Do not skip line. */
- error(tok,
- "Unterminated macro parameters");
- return tok;
- default:
- error(tok,
- "Bad token in macro parameters: " +
- tok.getText());
- return source_skipline(false);
- }
- tok = source_token_nonwhite();
- }
- }
- else {
- assert tok.getType() == ')' : "Expected ')'";
- args = Collections.emptyList();
- }
-
- m.setArgs(args);
- }
- else {
- /* For searching. */
- args = Collections.emptyList();
- source_untoken(tok);
- }
-
- /* Get an expansion for the macro, using indexOf. */
- boolean space = false;
- boolean paste = false;
- int idx;
-
- /* Ensure no space at start. */
- tok = source_token_nonwhite();
- EXPANSION: for (;;) {
- switch (tok.getType()) {
- case EOF:
- break EXPANSION;
- case NL:
- break EXPANSION;
-
- case CCOMMENT:
- case CPPCOMMENT:
- /* XXX This is where we implement GNU's cpp -CC. */
- // break;
- case WHITESPACE:
- if (!paste)
- space = true;
- break;
-
- /* Paste. */
- case PASTE:
- space = false;
- paste = true;
- m.addPaste(new Token(M_PASTE,
- tok.getLine(), tok.getColumn(),
- "#" + "#", null));
- break;
-
- /* Stringify. */
- case '#':
- if (space)
- m.addToken(Token.space);
- space = false;
- Token la = source_token_nonwhite();
- if (la.getType() == IDENTIFIER &&
- ((idx = args.indexOf(la.getText())) != -1)) {
- m.addToken(new Token(M_STRING,
- la.getLine(), la.getColumn(),
- "#" + la.getText(),
- Integer.valueOf(idx)));
- }
- else {
- m.addToken(tok);
- /* Allow for special processing. */
- source_untoken(la);
- }
- break;
-
- case IDENTIFIER:
- if (space)
- m.addToken(Token.space);
- space = false;
- paste = false;
- idx = args.indexOf(tok.getText());
- if (idx == -1)
- m.addToken(tok);
- else
- m.addToken(new Token(M_ARG,
- tok.getLine(), tok.getColumn(),
- tok.getText(),
- Integer.valueOf(idx)));
- break;
-
- default:
- if (space)
- m.addToken(Token.space);
- space = false;
- paste = false;
- m.addToken(tok);
- break;
- }
- tok = source_token();
- }
-
- if (getFeature(Feature.DEBUG))
- System.err.println("Defined macro " + m);
- addMacro(m);
-
- return tok; /* NL or EOF. */
- }
-
- private Token undef()
- throws IOException,
- LexerException {
- Token tok = source_token_nonwhite();
- 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());
- if (m != null) {
- /* XXX error if predefined */
- macros.remove(m.getName());
- }
- }
- return source_skipline(true);
- }
-
- /**
- * Attempts to include the given file.
- *
- * User code may override this method to implement a virtual
- * file system.
- */
- private boolean include(VirtualFile file)
- throws IOException,
- LexerException {
- // System.out.println("Try to include " + file);
- if (!file.isFile())
- return false;
- if (getFeature(Feature.DEBUG))
- System.err.println("pp: including " + file);
- push_source(file.getSource(), true);
- return true;
- }
-
- /**
- * Includes a file from an include path, by name.
- */
- private boolean include(Iterable<String> path, String name)
- throws IOException,
- LexerException {
- for (String dir : path) {
- VirtualFile file = filesystem.getFile(dir, name);
- if (include(file))
- return true;
- }
- return false;
- }
-
- /**
- * Handles an include directive.
- */
- private void include(String parent, int line,
- String name, boolean quoted)
- throws IOException,
- LexerException {
- VirtualFile pdir = null;
- if (quoted) {
- VirtualFile pfile = filesystem.getFile(parent);
- pdir = pfile.getParentFile();
- VirtualFile ifile = pdir.getChildFile(name);
- if (include(ifile))
- return;
- if (include(quoteincludepath, name))
- return;
- }
-
- if (include(sysincludepath, name))
- return;
-
- StringBuilder buf = new StringBuilder();
- buf.append("File not found: ").append(name);
- buf.append(" in");
- if (quoted) {
- buf.append(" .").append('(').append(pdir).append(')');
- for (String dir : quoteincludepath)
- buf.append(" ").append(dir);
- }
- for (String dir : sysincludepath)
- buf.append(" ").append(dir);
- error(line, 0, buf.toString());
- }
-
- private Token include(boolean next)
- throws IOException,
- LexerException {
- LexerSource lexer = (LexerSource)source;
- try {
- lexer.setInclude(true);
- Token tok = token_nonwhite();
-
- String name;
- boolean quoted;
-
- if (tok.getType() == STRING) {
- /* XXX Use the original text, not the value.
- * Backslashes must not be treated as escapes here. */
- StringBuilder buf = new StringBuilder((String)tok.getValue());
- HEADER: for (;;) {
- tok = token_nonwhite();
- switch (tok.getType()) {
- case STRING:
- buf.append((String)tok.getValue());
- break;
- case NL:
- case EOF:
- break HEADER;
- default:
- warning(tok,
- "Unexpected token on #"+"include line");
- return source_skipline(false);
- }
- }
- name = buf.toString();
- quoted = true;
- }
- else if (tok.getType() == HEADER) {
- name = (String)tok.getValue();
- quoted = false;
- tok = source_skipline(true);
- }
- else {
- error(tok,
- "Expected string or header, not " + tok.getText());
- switch (tok.getType()) {
- case NL:
- case EOF:
- return tok;
- default:
- /* Only if not a NL or EOF already. */
- return source_skipline(false);
- }
- }
-
- /* Do the inclusion. */
- include(source.getPath(), tok.getLine(), name, quoted);
-
- /* 'tok' is the 'nl' after the include. We use it after the
- * #line directive. */
- if (getFeature(Feature.LINEMARKERS))
- return line_token(1, source.getName(), " 1");
- return tok;
- }
- finally {
- lexer.setInclude(false);
- }
- }
-
- protected void pragma(Token name, List<Token> value)
- throws IOException,
- LexerException {
- warning(name, "Unknown #" + "pragma: " + name.getText());
- }
-
- private Token pragma()
- throws IOException,
- LexerException {
- Token name;
-
- NAME: for (;;) {
- Token tok = token();
- switch (tok.getType()) {
- case EOF:
- /* There ought to be a newline before EOF.
- * At least, in any skipline context. */
- /* XXX Are we sure about this? */
- warning(tok,
- "End of file in #" + "pragma");
- return tok;
- case NL:
- /* This may contain one or more newlines. */
- warning(tok,
- "Empty #" + "pragma");
- return tok;
- case CCOMMENT:
- case CPPCOMMENT:
- case WHITESPACE:
- continue NAME;
- case IDENTIFIER:
- name = tok;
- break NAME;
- default:
- return source_skipline(false);
- }
- }
-
- Token tok;
- List<Token> value = new ArrayList<Token>();
- VALUE: for (;;) {
- tok = token();
- switch (tok.getType()) {
- case EOF:
- /* There ought to be a newline before EOF.
- * At least, in any skipline context. */
- /* XXX Are we sure about this? */
- warning(tok,
- "End of file in #" + "pragma");
- break VALUE;
- case NL:
- /* This may contain one or more newlines. */
- break VALUE;
- case CCOMMENT:
- case CPPCOMMENT:
- break;
- case WHITESPACE:
- value.add(tok);
- break;
- default:
- value.add(tok);
- break;
- }
- }
-
- pragma(name, value);
-
- return tok; /* The NL. */
- }
-
- /* For #error and #warning. */
- private void error(Token pptok, boolean is_error)
- throws IOException,
- LexerException {
- StringBuilder buf = new StringBuilder();
- buf.append('#').append(pptok.getText()).append(' ');
- /* Peculiar construction to ditch first whitespace. */
- Token tok = source_token_nonwhite();
- ERROR: for (;;) {
- switch (tok.getType()) {
- case NL:
- case EOF:
- break ERROR;
- default:
- buf.append(tok.getText());
- break;
- }
- tok = source_token();
- }
- if (is_error)
- error(pptok, buf.toString());
- else
- warning(pptok, buf.toString());
- }
-
-
-
-
- /* This bypasses token() for #elif expressions.
- * If we don't do this, then isActive() == false
- * causes token() to simply chew the entire input line. */
- private Token expanded_token()
- throws IOException,
- LexerException {
- for (;;) {
- Token tok = source_token();
- // System.out.println("Source token is " + tok);
- if (tok.getType() == IDENTIFIER) {
- Macro m = macros.get(tok.getText());
- if (m == null)
- return tok;
- if (source.isExpanding(m))
- return tok;
- if (macro(m, tok))
- continue;
- }
- return tok;
- }
- }
-
- private Token expanded_token_nonwhite()
- throws IOException,
- LexerException {
- Token tok;
- do {
- tok = expanded_token();
- // System.out.println("expanded token is " + tok);
- } while (isWhite(tok));
- return tok;
- }
-
-
- private Token expr_token = null;
-
- private Token expr_token()
- throws IOException,
- LexerException {
- Token tok = expr_token;
-
- if (tok != null) {
- // System.out.println("ungetting");
- expr_token = null;
- }
- else {
- tok = expanded_token_nonwhite();
- // System.out.println("expt is " + tok);
-
- if (tok.getType() == IDENTIFIER &&
- tok.getText().equals("defined")) {
- Token la = source_token_nonwhite();
- boolean paren = false;
- if (la.getType() == '(') {
- paren = true;
- la = source_token_nonwhite();
- }
-
- // System.out.println("Core token is " + la);
-
- if (la.getType() != IDENTIFIER) {
- error(la,
- "defined() needs identifier, not " +
- la.getText());
- tok = new Token(NUMBER,
- la.getLine(), la.getColumn(),
- "0", new NumericValue(10, "0"));
- }
- else if (macros.containsKey(la.getText())) {
- // System.out.println("Found macro");
- tok = new Token(NUMBER,
- la.getLine(), la.getColumn(),
- "1", new NumericValue(10, "1"));
- }
- else {
- // System.out.println("Not found macro");
- tok = new Token(NUMBER,
- la.getLine(), la.getColumn(),
- "0", new NumericValue(10, "0"));
- }
-
- if (paren) {
- la = source_token_nonwhite();
- if (la.getType() != ')') {
- expr_untoken(la);
- error(la, "Missing ) in defined()");
- }
- }
- }
- }
-
- // System.out.println("expr_token returns " + tok);
-
- return tok;
- }
-
- private void expr_untoken(Token tok)
- throws LexerException {
- if (expr_token != null)
- throw new InternalException(
- "Cannot unget two expression tokens."
- );
- expr_token = tok;
- }
-
- private int expr_priority(Token op) {
- switch (op.getType()) {
- case '/': return 11;
- case '%': return 11;
- case '*': return 11;
- case '+': return 10;
- case '-': return 10;
- case LSH: return 9;
- case RSH: return 9;
- case '<': return 8;
- case '>': return 8;
- case LE: return 8;
- case GE: return 8;
- case EQ: return 7;
- case NE: return 7;
- case '&': return 6;
- case '^': return 5;
- case '|': return 4;
- case LAND: return 3;
- case LOR: return 2;
- case '?': return 1;
- default:
- // System.out.println("Unrecognised operator " + op);
- return 0;
- }
- }
-
- private long expr(int priority)
- throws IOException,
- LexerException {
- /*
- System.out.flush();
- (new Exception("expr(" + priority + ") called")).printStackTrace();
- System.err.flush();
- */
-
- Token tok = expr_token();
- long lhs, rhs;
-
- // System.out.println("Expr lhs token is " + tok);
-
- switch (tok.getType()) {
- case '(':
- lhs = expr(0);
- tok = expr_token();
- if (tok.getType() != ')') {
- expr_untoken(tok);
- error(tok, "missing ) in expression");
- return 0;
- }
- break;
-
- case '~': lhs = ~expr(11); break;
- case '!': lhs = expr(11) == 0 ? 1 : 0; break;
- case '-': lhs = -expr(11); break;
- case NUMBER:
- NumericValue value = (NumericValue)tok.getValue();
- lhs = value.longValue();
- break;
- case CHARACTER:
- lhs = (long)((Character)tok.getValue()).charValue();
- break;
- case IDENTIFIER:
- if (warnings.contains(Warning.UNDEF))
- warning(tok, "Undefined token '" + tok.getText() +
- "' encountered in conditional.");
- lhs = 0;
- break;
-
- default:
- expr_untoken(tok);
- error(tok,
- "Bad token in expression: " + tok.getText());
- return 0;
- }
-
- EXPR: for (;;) {
- // System.out.println("expr: lhs is " + lhs + ", pri = " + priority);
- Token op = expr_token();
- int pri = expr_priority(op); /* 0 if not a binop. */
- if (pri == 0 || priority >= pri) {
- expr_untoken(op);
- break EXPR;
- }
- rhs = expr(pri);
- // System.out.println("rhs token is " + rhs);
- switch (op.getType()) {
- case '/':
- if (rhs == 0) {
- error(op, "Division by zero");
- lhs = 0;
- }
- else {
- lhs = lhs / rhs;
- }
- break;
- case '%':
- if (rhs == 0) {
- error(op, "Modulus by zero");
- lhs = 0;
- }
- else {
- lhs = lhs % rhs;
- }
- break;
- case '*': lhs = lhs * rhs; break;
- case '+': lhs = lhs + rhs; break;
- case '-': lhs = lhs - rhs; break;
- case '<': lhs = lhs < rhs ? 1 : 0; break;
- case '>': lhs = lhs > rhs ? 1 : 0; break;
- case '&': lhs = lhs & rhs; break;
- case '^': lhs = lhs ^ rhs; break;
- case '|': lhs = lhs | rhs; break;
-
- case LSH: lhs = lhs << rhs; break;
- case RSH: lhs = lhs >> rhs; break;
- case LE: lhs = lhs <= rhs ? 1 : 0; break;
- case GE: lhs = lhs >= rhs ? 1 : 0; break;
- case EQ: lhs = lhs == rhs ? 1 : 0; break;
- case NE: lhs = lhs != rhs ? 1 : 0; break;
- case LAND: lhs = (lhs != 0) && (rhs != 0) ? 1 : 0; break;
- case LOR: lhs = (lhs != 0) || (rhs != 0) ? 1 : 0; break;
-
- case '?':
- /* XXX Handle this? */
-
- default:
- error(op,
- "Unexpected operator " + op.getText());
- return 0;
-
- }
- }
-
- /*
- System.out.flush();
- (new Exception("expr returning " + lhs)).printStackTrace();
- System.err.flush();
- */
- // System.out.println("expr returning " + lhs);
-
- return lhs;
- }
-
- private Token toWhitespace(Token tok) {
- String text = tok.getText();
- int len = text.length();
- boolean cr = false;
- int nls = 0;
-
- for (int i = 0; i < len; i++) {
- char c = text.charAt(i);
-
- switch (c) {
- case '\r':
- cr = true;
- nls++;
- break;
- case '\n':
- if (cr) {
- cr = false;
- break;
- }
- /* fallthrough */
- case '\u2028':
- case '\u2029':
- case '\u000B':
- case '\u000C':
- case '\u0085':
- cr = false;
- nls++;
- break;
- }
- }
-
- char[] cbuf = new char[nls];
- Arrays.fill(cbuf, '\n');
- return new Token(WHITESPACE,
- tok.getLine(), tok.getColumn(),
- new String(cbuf));
- }
-
- private final Token _token()
- throws IOException,
- LexerException {
-
- for (;;) {
- Token tok;
- if (!isActive()) {
- try {
- /* XXX Tell lexer to ignore warnings. */
- source.setActive(false);
- tok = source_token();
- }
- finally {
- /* XXX Tell lexer to stop ignoring warnings. */
- source.setActive(true);
- }
- switch (tok.getType()) {
- case HASH:
- case NL:
- case EOF:
- /* The preprocessor has to take action here. */
- break;
- case WHITESPACE:
- return tok;
- case CCOMMENT:
- case CPPCOMMENT:
- // Patch up to preserve whitespace.
- if (getFeature(Feature.KEEPALLCOMMENTS))
- return tok;
- if (!isActive())
- return toWhitespace(tok);
- if (getFeature(Feature.KEEPCOMMENTS))
- return tok;
- return toWhitespace(tok);
- default:
- // Return NL to preserve whitespace.
- /* XXX This might lose a comment. */
- return source_skipline(false);
- }
- }
- else {
- tok = source_token();
- }
-
- LEX: switch (tok.getType()) {
- case EOF:
- /* Pop the stacks. */
- return tok;
-
- case WHITESPACE:
- case NL:
- return tok;
-
- case CCOMMENT:
- case CPPCOMMENT:
- return tok;
-
- case '!': case '%': case '&':
- case '(': case ')': case '*':
- case '+': case ',': case '-':
- case '/': case ':': case ';':
- case '<': case '=': case '>':
- case '?': case '[': case ']':
- case '^': case '{': case '|':
- case '}': case '~': case '.':
-
- /* From Olivier Chafik for Objective C? */
- case '@':
- /* The one remaining ASCII, might as well. */
- case '`':
-
- // case '#':
-
- case AND_EQ:
- case ARROW:
- case CHARACTER:
- case DEC:
- case DIV_EQ:
- case ELLIPSIS:
- case EQ:
- case GE:
- case HEADER: /* Should only arise from include() */
- case INC:
- case LAND:
- case LE:
- case LOR:
- case LSH:
- case LSH_EQ:
- case SUB_EQ:
- case MOD_EQ:
- case MULT_EQ:
- case NE:
- case OR_EQ:
- case PLUS_EQ:
- case RANGE:
- case RSH:
- case RSH_EQ:
- case STRING:
- case XOR_EQ:
- return tok;
-
- case NUMBER:
- return tok;
-
- case IDENTIFIER:
- Macro m = macros.get(tok.getText());
- if (m == null)
- return tok;
- if (source.isExpanding(m))
- return tok;
- if (macro(m, tok))
- break;
- return tok;
-
- case P_LINE:
- if (getFeature(Feature.LINEMARKERS))
- return tok;
- break;
-
- case INVALID:
- if (getFeature(Feature.CSYNTAX))
- error(tok, String.valueOf(tok.getValue()));
- return tok;
-
- default:
- throw new InternalException("Bad token " + tok);
- // break;
-
- case HASH:
- tok = source_token_nonwhite();
- // (new Exception("here")).printStackTrace();
- switch (tok.getType()) {
- case NL:
- break LEX; /* Some code has #\n */
- case IDENTIFIER:
- break;
- default:
- error(tok,
- "Preprocessor directive not a word " +
- tok.getText());
- return source_skipline(false);
- }
- Integer _ppcmd = ppcmds.get(tok.getText());
- if (_ppcmd == null) {
- error(tok,
- "Unknown preprocessor directive " +
- tok.getText());
- return source_skipline(false);
- }
- int ppcmd = _ppcmd.intValue();
-
- PP: switch (ppcmd) {
-
- case PP_DEFINE:
- if (!isActive())
- return source_skipline(false);
- else
- return define();
- // break;
-
- case PP_UNDEF:
- if (!isActive())
- return source_skipline(false);
- else
- return undef();
- // break;
-
- case PP_INCLUDE:
- if (!isActive())
- return source_skipline(false);
- else
- return include(false);
- // break;
- case PP_INCLUDE_NEXT:
- if (!isActive())
- return source_skipline(false);
- if (!getFeature(Feature.INCLUDENEXT)) {
- error(tok,
- "Directive include_next not enabled"
- );
- return source_skipline(false);
- }
- return include(true);
- // break;
-
- case PP_WARNING:
- case PP_ERROR:
- if (!isActive())
- return source_skipline(false);
- else
- error(tok, ppcmd == PP_ERROR);
- break;
-
- case PP_IF:
- push_state();
- if (!isActive()) {
- return source_skipline(false);
- }
- expr_token = null;
- states.peek().setActive(expr(0) != 0);
- tok = expr_token(); /* unget */
- if (tok.getType() == NL)
- return tok;
- return source_skipline(true);
- // break;
-
- case PP_ELIF:
- State state = states.peek();
- if (false) {
- /* Check for 'if' */ ;
- }
- else if (state.sawElse()) {
- error(tok,
- "#elif after #" + "else");
- return source_skipline(false);
- }
- else if (!state.isParentActive()) {
- /* Nested in skipped 'if' */
- return source_skipline(false);
- }
- else if (state.isActive()) {
- /* The 'if' part got executed. */
- state.setParentActive(false);
- /* This is like # else # if but with
- * only one # end. */
- state.setActive(false);
- return source_skipline(false);
- }
- else {
- expr_token = null;
- state.setActive(expr(0) != 0);
- tok = expr_token(); /* unget */
- if (tok.getType() == NL)
- return tok;
- return source_skipline(true);
- }
- // break;
-
- case PP_ELSE:
- state = states.peek();
- 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());
- return source_skipline(warnings.contains(Warning.ENDIF_LABELS));
- }
- // break;
-
- case PP_IFDEF:
- push_state();
- if (!isActive()) {
- return source_skipline(false);
- }
- else {
- tok = source_token_nonwhite();
- // System.out.println("ifdef " + tok);
- if (tok.getType() != IDENTIFIER) {
- error(tok,
- "Expected identifier, not " +
- tok.getText());
- return source_skipline(false);
- }
- else {
- String text = tok.getText();
- boolean exists =
- macros.containsKey(text);
- states.peek().setActive(exists);
- return source_skipline(true);
- }
- }
- // break;
-
- case PP_IFNDEF:
- push_state();
- if (!isActive()) {
- return source_skipline(false);
- }
- else {
- tok = source_token_nonwhite();
- if (tok.getType() != IDENTIFIER) {
- error(tok,
- "Expected identifier, not " +
- tok.getText());
- return source_skipline(false);
- }
- else {
- String text = tok.getText();
- boolean exists =
- macros.containsKey(text);
- states.peek().setActive(!exists);
- return source_skipline(true);
- }
- }
- // break;
-
- case PP_ENDIF:
- pop_state();
- return source_skipline(warnings.contains(Warning.ENDIF_LABELS));
- // break;
-
- case PP_LINE:
- return source_skipline(false);
- // break;
-
- case PP_PRAGMA:
- if (!isActive())
- return source_skipline(false);
- return pragma();
- // break;
-
- default:
- /* Actual unknown directives are
- * processed above. If we get here,
- * we succeeded the map lookup but
- * failed to handle it. Therefore,
- * this is (unconditionally?) fatal. */
- // if (isActive()) /* XXX Could be warning. */
- throw new InternalException(
- "Internal error: Unknown directive "
- + tok);
- // return source_skipline(false);
- }
-
-
- }
- }
- }
-
- private Token token_nonwhite()
- throws IOException,
- LexerException {
- Token tok;
- do {
- tok = _token();
- } while (isWhite(tok));
- return tok;
- }
-
- /**
- * Returns the next preprocessor token.
- *
- * @see Token
- * @throws LexerException if a preprocessing error occurs.
- * @throws InternalException if an unexpected error condition arises.
- */
- public Token token()
- throws IOException,
- LexerException {
- Token tok = _token();
- if (getFeature(Feature.DEBUG))
- System.err.println("pp: Returning " + tok);
- return tok;
- }
-
-#set ($i = 1) /* First ppcmd is 1, not 0. */
-#set ($ppcmds = [ "define", "elif", "else", "endif", "error", "if", "ifdef", "ifndef", "include", "line", "pragma", "undef", "warning", "include_next", "import" ])
-#foreach ($ppcmd in $ppcmds)
- private static final int PP_$ppcmd.toUpperCase() = $i;
-#set ($i = $i + 1)
-#end
-
- private static final Map<String,Integer> ppcmds =
- new HashMap<String,Integer>();
-
- static {
-#foreach ($ppcmd in $ppcmds)
- ppcmds.put("$ppcmd", Integer.valueOf(PP_$ppcmd.toUpperCase()));
-#end
- }
-
-
- public String toString() {
- StringBuilder buf = new StringBuilder();
-
- Source s = getSource();
- while (s != null) {
- buf.append(" -> ").append(String.valueOf(s)).append("\n");
- s = s.getParent();
- }
-
- Map<String,Macro> macros = getMacros();
- List<String> keys = new ArrayList<String>(
- macros.keySet()
- );
- Collections.sort(keys);
- Iterator<String> mt = keys.iterator();
- while (mt.hasNext()) {
- String key = mt.next();
- Macro macro = macros.get(key);
- buf.append("#").append("macro ").append(macro).append("\n");
- }
-
- return buf.toString();
- }
-
- public void close()
- throws IOException {
- {
- Source s = source;
- while (s != null) {
- s.close();
- s = s.getParent();
- }
- }
- for (Source s : inputs) {
- s.close();
- }
- }
-
-}
diff --git a/src/java/org/anarres/cpp/PreprocessorListener.java b/src/java/org/anarres/cpp/PreprocessorListener.java
deleted file mode 100644
index d149a70..0000000
--- a/src/java/org/anarres/cpp/PreprocessorListener.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.File;
-
-/**
- * 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 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);
- }
-
- /**
- * 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) {
- }
-
-}
diff --git a/src/java/org/anarres/cpp/Source.java b/src/java/org/anarres/cpp/Source.java
deleted file mode 100644
index 5f50a86..0000000
--- a/src/java/org/anarres/cpp/Source.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-import java.io.BufferedReader;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PushbackReader;
-import java.io.Reader;
-import java.io.StringReader;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import static org.anarres.cpp.Token.*;
-
-/**
- * An input to the Preprocessor.
- *
- * Inputs may come from Files, Strings or other sources. The
- * preprocessor maintains a stack of Sources. Operations such as
- * file inclusion or token pasting will push a new source onto
- * the Preprocessor stack. Sources pop from the stack when they
- * are exhausted; this may be transparent or explicit.
- *
- * BUG: Error messages are not handled properly.
- */
-public abstract class Source implements Iterable<Token>, Closeable {
- private Source parent;
- private boolean autopop;
- private PreprocessorListener listener;
- private boolean active;
- private boolean werror;
-
- /* LineNumberReader */
-
-/*
- // We can't do this, since we would lose the LexerException
- private class Itr implements Iterator {
- private Token next = null;
- private void advance() {
- try {
- if (next != null)
- next = token();
- }
- catch (IOException e) {
- throw new UnsupportedOperationException(
- "Failed to advance token iterator: " +
- e.getMessage()
- );
- }
- }
- public boolean hasNext() {
- return next.getType() != EOF;
- }
- public Token next() {
- advance();
- Token t = next;
- next = null;
- return t;
- }
- public void remove() {
- throw new UnsupportedOperationException(
- "Cannot remove tokens from a Source."
- );
- }
- }
-*/
-
- public Source() {
- this.parent = null;
- this.autopop = false;
- this.listener = null;
- this.active = true;
- this.werror = false;
- }
-
- /**
- * Sets the parent source of this source.
- *
- * Sources form a singly linked list.
- */
- /* pp */ void setParent(Source parent, boolean autopop) {
- this.parent = parent;
- this.autopop = autopop;
- }
-
- /**
- * Returns the parent source of this source.
- *
- * Sources form a singly linked list.
- */
- /* pp */ final Source getParent() {
- return parent;
- }
-
- // @OverrideMustInvoke
- /* pp */ void init(Preprocessor pp) {
- setListener(pp.getListener());
- this.werror = pp.getWarnings().contains(Warning.ERROR);
- }
-
- /**
- * Sets the listener for this Source.
- *
- * Normally this is set by the Preprocessor when a Source is
- * used, but if you are using a Source as a standalone object,
- * you may wish to call this.
- */
- public void setListener(PreprocessorListener pl) {
- this.listener = pl;
- }
-
- /**
- * Returns the File currently being lexed.
- *
- * If this Source is not a {@link FileLexerSource}, then
- * it will ask the parent Source, and so forth recursively.
- * If no Source on the stack is a FileLexerSource, returns null.
- */
- /* pp */ String getPath() {
- Source parent = getParent();
- if (parent != null)
- return parent.getPath();
- return null;
- }
-
- /**
- * Returns the human-readable name of the current Source.
- */
- /* pp */ String getName() {
- Source parent = getParent();
- if (parent != null)
- return parent.getName();
- return null;
- }
-
- /**
- * Returns the current line number within this Source.
- */
- public int getLine() {
- Source parent = getParent();
- if (parent == null)
- return 0;
- return parent.getLine();
- }
-
- /**
- * Returns the current column number within this Source.
- */
- public int getColumn() {
- Source parent = getParent();
- if (parent == null)
- return 0;
- return parent.getColumn();
- }
-
- /**
- * Returns true if this Source is expanding the given macro.
- *
- * This is used to prevent macro recursion.
- */
- /* pp */ boolean isExpanding(Macro m) {
- Source parent = getParent();
- if (parent != null)
- return parent.isExpanding(m);
- return false;
- }
-
- /**
- * Returns true if this Source should be transparently popped
- * from the input stack.
- *
- * Examples of such sources are macro expansions.
- */
- /* pp */ boolean isAutopop() {
- return autopop;
- }
-
- /**
- * Returns true if this source has line numbers.
- */
- /* pp */ boolean isNumbered() {
- return false;
- }
-
- /* This is an incredibly lazy way of disabling warnings when
- * the source is not active. */
- /* pp */ void setActive(boolean b) {
- this.active = b;
- }
-
- /* pp */ boolean isActive() {
- return active;
- }
-
- /**
- * Returns the next Token parsed from this input stream.
- *
- * @see Token
- */
- public abstract Token token()
- throws IOException,
- LexerException;
-
- /**
- * Returns a token iterator for this Source.
- */
- public Iterator<Token> iterator() {
- return new SourceIterator(this);
- }
-
- /**
- * Skips tokens until the end of line.
- *
- * @param white true if only whitespace is permitted on the
- * remainder of the line.
- * @return the NL token.
- */
- public Token skipline(boolean white)
- throws IOException,
- LexerException {
- for (;;) {
- Token tok = token();
- switch (tok.getType()) {
- case EOF:
- /* There ought to be a newline before EOF.
- * At least, in any skipline context. */
- /* XXX Are we sure about this? */
- warning(tok.getLine(), tok.getColumn(),
- "No newline before end of file");
- return new Token(NL,
- tok.getLine(), tok.getColumn(),
- "\n");
- // return tok;
- case NL:
- /* This may contain one or more newlines. */
- return tok;
- case CCOMMENT:
- case CPPCOMMENT:
- case WHITESPACE:
- break;
- default:
- /* XXX Check white, if required. */
- if (white)
- warning(tok.getLine(), tok.getColumn(),
- "Unexpected nonwhite token");
- break;
- }
- }
- }
-
- protected void error(int line, int column, String msg)
- throws LexerException {
- if (listener != null)
- listener.handleError(this, line, column, msg);
- else
- throw new LexerException("Error at " + line + ":" + column + ": " + msg);
- }
-
- protected void warning(int line, int column, String msg)
- throws LexerException {
- if (werror)
- error(line, column, msg);
- else if (listener != null)
- listener.handleWarning(this, line, column, msg);
- else
- throw new LexerException("Warning at " + line + ":" + column + ": " + msg);
- }
-
- public void close()
- throws IOException {
- }
-
-}
diff --git a/src/java/org/anarres/cpp/Token.java b/src/java/org/anarres/cpp/Token.java
deleted file mode 100644
index a2cac39..0000000
--- a/src/java/org/anarres/cpp/Token.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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.
- */
-
-package org.anarres.cpp;
-
-/**
- * A Preprocessor token.
- *
- * @see Preprocessor
- */
-public final class Token {
-
- // public static final int EOF = -1;
-
- private int type;
- private int line;
- private int column;
- private Object value;
- private String text;
-
- public Token(int type, int line, int column,
- String text, Object value) {
- this.type = type;
- this.line = line;
- this.column = column;
- this.text = text;
- this.value = value;
- }
-
- public Token(int type, int line, int column, String text) {
- this(type, line, column, text, null);
- }
-
- /* pp */ Token(int type, String text, Object value) {
- this(type, -1, -1, text, value);
- }
-
- /* pp */ Token(int type, String text) {
- this(type, text, null);
- }
-
- /* pp */ Token(int type) {
- this(type, type < _TOKENS ? texts[type] : "TOK" + type);
- }
-
- /**
- * Returns the semantic type of this token.
- */
- public int getType() {
- return type;
- }
-
- /* pp */ void setLocation(int line, int column) {
- this.line = line;
- this.column = column;
- }
-
- /**
- * Returns the line at which this token started.
- *
- * Lines are numbered from zero.
- */
- public int getLine() {
- return line;
- }
-
- /**
- * Returns the column at which this token started.
- *
- * Columns are numbered from zero.
- */
- public int getColumn() {
- return column;
- }
-
- /**
- * Returns the original or generated text of this token.
- *
- * This is distinct from the semantic value of the token.
- *
- * @see #getValue()
- */
- public String getText() {
- return text;
- }
-
- /**
- * Returns the semantic value of this token.
- *
- * For strings, this is the parsed String.
- * For integers, this is an Integer object.
- * For other token types, as appropriate.
- *
- * @see #getText()
- */
- public Object getValue() {
- return value;
- }
-
- /**
- * Returns a description of this token, for debugging purposes.
- */
- public String toString() {
- StringBuilder buf = new StringBuilder();
-
- buf.append('[').append(getTokenName(type));
- if (line != -1) {
- buf.append('@').append(line);
- if (column != -1)
- buf.append(',').append(column);
- }
- buf.append("]:");
- if (text != null)
- buf.append('"').append(text).append('"');
- else if (type > 3 && type < 256)
- buf.append( (char)type );
- else
- buf.append('<').append(type).append('>');
- if (value != null)
- buf.append('=').append(value);
- return buf.toString();
- }
-
- /**
- * Returns the descriptive name of the given token type.
- *
- * This is mostly used for stringification and debugging.
- */
- public static final String getTokenName(int type) {
- if (type < 0)
- return "Invalid" + type;
- if (type >= names.length)
- return "Invalid" + type;
- if (names[type] == null)
- return "Unknown" + type;
- return names[type];
- }
-
-#set ($i = 257)
-#set ($tokens = [ "AND_EQ", "ARROW", "CHARACTER", "CCOMMENT", "CPPCOMMENT", "DEC", "DIV_EQ", "ELLIPSIS", "EOF", "EQ", "GE", "HASH", "HEADER", "IDENTIFIER", "INC", "NUMBER", "LAND", "LAND_EQ", "LE", "LITERAL", "LOR", "LOR_EQ", "LSH", "LSH_EQ", "MOD_EQ", "MULT_EQ", "NE", "NL", "OR_EQ", "PASTE", "PLUS_EQ", "RANGE", "RSH", "RSH_EQ", "SQSTRING", "STRING", "SUB_EQ", "WHITESPACE", "XOR_EQ", "M_ARG", "M_PASTE", "M_STRING", "P_LINE", "INVALID" ])
-#foreach ($token in $tokens)
- /** The token type $token. */
- public static final int $token = $i;
-#set ($i = $i + 1)
-#end
- /**
- * The number of possible semantic token types.
- *
- * Please note that not all token types below 255 are used.
- */
- public static final int _TOKENS = $i;
-
- /** The position-less space token. */
- /* pp */ static final Token space = new Token(WHITESPACE, -1, -1, " ");
-
- private static final String[] names = new String[_TOKENS];
- private static final String[] texts = new String[_TOKENS];
- static {
- for (int i = 0; i < 255; i++) {
- texts[i] = String.valueOf(new char[] { (char)i });
- names[i] = texts[i];
- }
-
- texts[AND_EQ] = "&=";
- texts[ARROW] = "->";
- texts[DEC] = "--";
- texts[DIV_EQ] = "/=";
- texts[ELLIPSIS] = "...";
- texts[EQ] = "==";
- texts[GE] = ">=";
- texts[HASH] = "#";
- texts[INC] = "++";
- texts[LAND] = "&&";
- texts[LAND_EQ] = "&&=";
- texts[LE] = "<=";
- texts[LOR] = "||";
- texts[LOR_EQ] = "||=";
- texts[LSH] = "<<";
- texts[LSH_EQ] = "<<=";
- texts[MOD_EQ] = "%=";
- texts[MULT_EQ] = "*=";
- texts[NE] = "!=";
- texts[NL] = "\n";
- texts[OR_EQ] = "|=";
- /* We have to split the two hashes or Velocity eats them. */
- texts[PASTE] = "#" + "#";
- texts[PLUS_EQ] = "+=";
- texts[RANGE] = "..";
- texts[RSH] = ">>";
- texts[RSH_EQ] = ">>=";
- texts[SUB_EQ] = "-=";
- texts[XOR_EQ] = "^=";
-
-#foreach ($token in $tokens)
- names[$token] = "$token";
-#end
- }
-
-}
diff --git a/src/java/org/anarres/cpp/Argument.java b/src/main/java/org/anarres/cpp/Argument.java
index a868db5..a868db5 100644
--- a/src/java/org/anarres/cpp/Argument.java
+++ b/src/main/java/org/anarres/cpp/Argument.java
diff --git a/src/main/java/org/anarres/cpp/ChrootFileSystem.java b/src/main/java/org/anarres/cpp/ChrootFileSystem.java
new file mode 100644
index 0000000..1cec184
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/ChrootFileSystem.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A virtual filesystem implementation using java.io in a virtual
+ * chroot.
+ */
+public class ChrootFileSystem implements VirtualFileSystem {
+
+ private File root;
+
+ public ChrootFileSystem(File root) {
+ this.root = root;
+ }
+
+ @Override
+ public VirtualFile getFile(String path) {
+ return new ChrootFile(path);
+ }
+
+ @Override
+ public VirtualFile getFile(String dir, String name) {
+ return new ChrootFile(dir, name);
+ }
+
+ private class ChrootFile extends File implements VirtualFile {
+
+ private File rfile;
+
+ public ChrootFile(String path) {
+ super(path);
+ }
+
+ public ChrootFile(String dir, String name) {
+ super(dir, name);
+ }
+
+ /* private */
+ public ChrootFile(File dir, String name) {
+ super(dir, name);
+ }
+
+ @Override
+ public ChrootFile getParentFile() {
+ return new ChrootFile(getParent());
+ }
+
+ @Override
+ public ChrootFile getChildFile(String name) {
+ return new ChrootFile(this, name);
+ }
+
+ @Override
+ public boolean isFile() {
+ File real = new File(root, getPath());
+ return real.isFile();
+ }
+
+ @Override
+ public Source getSource() throws IOException {
+ return new FileLexerSource(new File(root, getPath()),
+ getPath());
+ }
+ }
+
+}
diff --git a/src/main/java/org/anarres/cpp/CppReader.java b/src/main/java/org/anarres/cpp/CppReader.java
new file mode 100644
index 0000000..7307f56
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/CppReader.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * A Reader wrapper around the Preprocessor.
+ *
+ * This is a utility class to provide a transparent {@link Reader}
+ * which preprocesses the input text.
+ *
+ * @see Preprocessor
+ * @see Reader
+ */
+public class CppReader extends Reader implements Closeable {
+
+ private Preprocessor cpp;
+ private String token;
+ private int idx;
+
+ public CppReader(final Reader r) {
+ cpp = new Preprocessor(new LexerSource(r, true) {
+ @Override
+ public String getName() {
+ return "<CppReader Input@"
+ + System.identityHashCode(r) + ">";
+ }
+ });
+ token = "";
+ idx = 0;
+ }
+
+ public CppReader(Preprocessor p) {
+ cpp = p;
+ token = "";
+ idx = 0;
+ }
+
+ /**
+ * Returns the Preprocessor used by this CppReader.
+ */
+ public Preprocessor getPreprocessor() {
+ return cpp;
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * This is a convnience method.
+ */
+ public void addMacro(String name)
+ throws LexerException {
+ cpp.addMacro(name);
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * This is a convnience method.
+ */
+ public void addMacro(String name, String value)
+ throws LexerException {
+ cpp.addMacro(name, value);
+ }
+
+ private boolean refill()
+ throws IOException {
+ try {
+ assert cpp != null : "cpp is null : was it closed?";
+ if (token == null)
+ return false;
+ while (idx >= token.length()) {
+ Token tok = cpp.token();
+ switch (tok.getType()) {
+ case EOF:
+ token = null;
+ return false;
+ case CCOMMENT:
+ case CPPCOMMENT:
+ if (!cpp.getFeature(Feature.KEEPCOMMENTS)) {
+ token = " ";
+ break;
+ }
+ default:
+ token = tok.getText();
+ break;
+ }
+ idx = 0;
+ }
+ return true;
+ } catch (LexerException e) {
+ /* Never happens.
+ if (e.getCause() instanceof IOException)
+ throw (IOException)e.getCause();
+ */
+ IOException ie = new IOException(String.valueOf(e));
+ ie.initCause(e);
+ throw ie;
+ }
+ }
+
+ @Override
+ public int read()
+ throws IOException {
+ if (!refill())
+ return -1;
+ return token.charAt(idx++);
+ }
+
+ /* XXX Very slow and inefficient. */
+ public int read(char cbuf[], int off, int len)
+ throws IOException {
+ if (token == null)
+ return -1;
+ for (int i = 0; i < len; i++) {
+ int ch = read();
+ if (ch == -1)
+ return i;
+ cbuf[off + i] = (char) ch;
+ }
+ return len;
+ }
+
+ @Override
+ public void close()
+ throws IOException {
+ if (cpp != null) {
+ cpp.close();
+ cpp = null;
+ }
+ token = null;
+ }
+
+}
diff --git a/src/main/java/org/anarres/cpp/CppTask.java b/src/main/java/org/anarres/cpp/CppTask.java
new file mode 100644
index 0000000..5e17786
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/CppTask.java
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Copy;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * An ant task for jcpp.
+ */
+public class CppTask extends Copy {
+
+ private class Listener extends PreprocessorListener {
+
+ @Override
+ protected void print(String msg) {
+ log(msg);
+ }
+ }
+
+ public static class Macro {
+
+ private String name;
+ private String value;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ private final Listener listener = new Listener();
+ private final List<Macro> macros = new ArrayList<Macro>();
+ private Path systemincludepath;
+ private Path localincludepath;
+
+ public void addMacro(Macro macro) {
+ macros.add(macro);
+ }
+
+ public void addSystemincludepath(Path path) {
+ if (systemincludepath == null)
+ systemincludepath = new Path(getProject());
+ systemincludepath.add(path);
+ }
+
+ public void addLocalincludepath(Path path) {
+ if (localincludepath == null)
+ localincludepath = new Path(getProject());
+ localincludepath.add(path);
+ }
+
+ /*
+ public void execute() {
+ FileWriter writer = null;
+ try {
+ if (input == null)
+ throw new BuildException("Input not specified");
+ if (output == null)
+ throw new BuildException("Output not specified");
+ cpp.addInput(this.input);
+ writer = new FileWriter(this.output);
+ for (;;) {
+ Token tok = cpp.token();
+ if (tok != null && tok.getType() == Token.EOF)
+ break;
+ writer.write(tok.getText());
+ }
+ }
+ catch (Exception e) {
+ throw new BuildException(e);
+ }
+ finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ }
+ catch (IOException e) {
+ }
+ }
+ }
+ }
+ */
+ private void preprocess(File input, File output) throws Exception {
+ Preprocessor cpp = new Preprocessor();
+ cpp.setListener(listener);
+ for (Macro macro : macros)
+ cpp.addMacro(macro.getName(), macro.getValue());
+ if (systemincludepath != null)
+ cpp.setSystemIncludePath(Arrays.asList(systemincludepath.list()));
+ if (localincludepath != null)
+ cpp.setQuoteIncludePath(Arrays.asList(localincludepath.list()));
+
+ File dir = output.getParentFile();
+ if (!dir.exists()) {
+ if (!dir.mkdirs())
+ throw new BuildException("Failed to make parent directory " + dir);
+ } else if (!dir.isDirectory()) {
+ throw new BuildException("Parent directory of output file " + output + " exists, but is not a directory.");
+ }
+ FileWriter writer = null;
+ try {
+ if (input == null)
+ throw new BuildException("Input not specified");
+ if (output == null)
+ throw new BuildException("Output not specified");
+ cpp.addInput(input);
+ writer = new FileWriter(output);
+ for (;;) {
+ Token tok = cpp.token();
+ if (tok == null)
+ break;
+ if (tok.getType() == Token.EOF)
+ break;
+ writer.write(tok.getText());
+ }
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void doFileOperations() {
+ if (fileCopyMap.size() > 0) {
+ log("Copying " + fileCopyMap.size()
+ + " file" + (fileCopyMap.size() == 1 ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+
+ Enumeration<String> e = fileCopyMap.keys();
+
+ while (e.hasMoreElements()) {
+ String fromFile = e.nextElement();
+ String[] toFiles = (String[]) fileCopyMap.get(fromFile);
+
+ for (String toFile : toFiles) {
+ if (fromFile.equals(toFile)) {
+ log("Skipping self-copy of " + fromFile, verbosity);
+ continue;
+ }
+
+ try {
+ log("Copying " + fromFile + " to " + toFile, verbosity);
+
+ FilterSetCollection executionFilters
+ = new FilterSetCollection();
+ if (filtering) {
+ executionFilters
+ .addFilterSet(getProject().getGlobalFilterSet());
+ }
+ for (Enumeration filterEnum = getFilterSets().elements();
+ filterEnum.hasMoreElements();) {
+ executionFilters
+ .addFilterSet((FilterSet) filterEnum.nextElement());
+ }
+
+ File srcFile = new File(fromFile);
+ File dstFile = new File(toFile);
+ preprocess(srcFile, dstFile);
+ } catch (Exception ioe) {
+ // ioe.printStackTrace();
+ String msg = "Failed to copy " + fromFile + " to " + toFile
+ + " due to " + ioe.getMessage();
+ File targetFile = new File(toFile);
+ if (targetFile.exists() && !targetFile.delete()) {
+ msg += " and I couldn't delete the corrupt " + toFile;
+ }
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/Feature.java b/src/main/java/org/anarres/cpp/Feature.java
index 04a68b7..04a68b7 100644
--- a/src/java/org/anarres/cpp/Feature.java
+++ b/src/main/java/org/anarres/cpp/Feature.java
diff --git a/src/java/org/anarres/cpp/FileLexerSource.java b/src/main/java/org/anarres/cpp/FileLexerSource.java
index db5f9e0..db5f9e0 100644
--- a/src/java/org/anarres/cpp/FileLexerSource.java
+++ b/src/main/java/org/anarres/cpp/FileLexerSource.java
diff --git a/src/main/java/org/anarres/cpp/FixedTokenSource.java b/src/main/java/org/anarres/cpp/FixedTokenSource.java
new file mode 100644
index 0000000..a1c7500
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/FixedTokenSource.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.IOException;
+
+import java.util.Arrays;
+import java.util.List;
+
+/* pp */ class FixedTokenSource extends Source {
+
+ private static final Token EOF
+ = new Token(Token.EOF, "<ts-eof>");
+
+ private final List<Token> tokens;
+ private int idx;
+
+ /* pp */ FixedTokenSource(Token... tokens) {
+ this.tokens = Arrays.asList(tokens);
+ this.idx = 0;
+ }
+
+ /* pp */ FixedTokenSource(List<Token> tokens) {
+ this.tokens = tokens;
+ this.idx = 0;
+ }
+
+ @Override
+ public Token token()
+ throws IOException,
+ LexerException {
+ if (idx >= tokens.size())
+ return EOF;
+ return tokens.get(idx++);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("constant token stream ").append(tokens);
+ Source parent = getParent();
+ if (parent != null)
+ buf.append(" in ").append(String.valueOf(parent));
+ return buf.toString();
+ }
+}
diff --git a/src/java/org/anarres/cpp/InputLexerSource.java b/src/main/java/org/anarres/cpp/InputLexerSource.java
index 0931dc4..3e1dcb3 100644
--- a/src/java/org/anarres/cpp/InputLexerSource.java
+++ b/src/main/java/org/anarres/cpp/InputLexerSource.java
@@ -14,7 +14,6 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
package org.anarres.cpp;
import java.io.BufferedReader;
@@ -22,11 +21,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
-import java.util.List;
-import java.util.Iterator;
-
-import static org.anarres.cpp.Token.*;
-
/**
* A {@link Source} which lexes a file.
*
@@ -35,34 +29,36 @@ import static org.anarres.cpp.Token.*;
* @see Source
*/
public class InputLexerSource extends LexerSource {
- /**
- * Creates a new Source for lexing the given Reader.
- *
- * Preprocessor directives are honoured within the file.
- */
- public InputLexerSource(InputStream input)
- throws IOException {
- super(
- new BufferedReader(
- new InputStreamReader(
- input
- )
- ),
- true
- );
- }
- @Override
- /* pp */ String getPath() {
- return "<standard-input>";
- }
+ /**
+ * Creates a new Source for lexing the given Reader.
+ *
+ * Preprocessor directives are honoured within the file.
+ */
+ public InputLexerSource(InputStream input)
+ throws IOException {
+ super(
+ new BufferedReader(
+ new InputStreamReader(
+ input
+ )
+ ),
+ true
+ );
+ }
+
+ @Override
+ /* pp */ String getPath() {
+ return "<standard-input>";
+ }
- @Override
- /* pp */ String getName() {
- return "standard input";
- }
+ @Override
+ /* pp */ String getName() {
+ return "standard input";
+ }
- public String toString() {
- return getPath();
- }
+ @Override
+ public String toString() {
+ return getPath();
+ }
}
diff --git a/src/java/org/anarres/cpp/InternalException.java b/src/main/java/org/anarres/cpp/InternalException.java
index ac53017..ac53017 100644
--- a/src/java/org/anarres/cpp/InternalException.java
+++ b/src/main/java/org/anarres/cpp/InternalException.java
diff --git a/src/main/java/org/anarres/cpp/JavaFileSystem.java b/src/main/java/org/anarres/cpp/JavaFileSystem.java
new file mode 100644
index 0000000..a60271d
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/JavaFileSystem.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A virtual filesystem implementation using java.io.
+ */
+public class JavaFileSystem implements VirtualFileSystem {
+
+ @Override
+ public VirtualFile getFile(String path) {
+ return new JavaFile(path);
+ }
+
+ @Override
+ public VirtualFile getFile(String dir, String name) {
+ return new JavaFile(dir, name);
+ }
+
+ private class JavaFile extends File implements VirtualFile {
+
+ public JavaFile(String path) {
+ super(path);
+ }
+
+ public JavaFile(String dir, String name) {
+ super(dir, name);
+ }
+
+ /* private */
+ public JavaFile(File dir, String name) {
+ super(dir, name);
+ }
+
+ /*
+ @Override
+ public String getPath() {
+ return getCanonicalPath();
+ }
+ */
+ @Override
+ public JavaFile getParentFile() {
+ String parent = getParent();
+ if (parent != null)
+ return new JavaFile(parent);
+ File absolute = getAbsoluteFile();
+ parent = absolute.getParent();
+ /*
+ if (parent == null)
+ return null;
+ */
+ return new JavaFile(parent);
+ }
+
+ @Override
+ public JavaFile getChildFile(String name) {
+ return new JavaFile(this, name);
+ }
+
+ @Override
+ public Source getSource() throws IOException {
+ return new FileLexerSource(this);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/anarres/cpp/JoinReader.java b/src/main/java/org/anarres/cpp/JoinReader.java
new file mode 100644
index 0000000..0286577
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/JoinReader.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.Closeable;
+import java.io.Reader;
+import java.io.IOException;
+
+/* pp */ class JoinReader /* extends Reader */ implements Closeable {
+
+ private final Reader in;
+
+ private PreprocessorListener listener;
+ private LexerSource source;
+ private boolean trigraphs;
+ private boolean warnings;
+
+ private int newlines;
+ private boolean flushnl;
+ private int[] unget;
+ private int uptr;
+
+ public JoinReader(Reader in, boolean trigraphs) {
+ this.in = in;
+ this.trigraphs = trigraphs;
+ this.newlines = 0;
+ this.flushnl = false;
+ this.unget = new int[2];
+ this.uptr = 0;
+ }
+
+ public JoinReader(Reader in) {
+ this(in, false);
+ }
+
+ public void setTrigraphs(boolean enable, boolean warnings) {
+ this.trigraphs = enable;
+ this.warnings = warnings;
+ }
+
+ /* pp */ void init(Preprocessor pp, LexerSource s) {
+ this.listener = pp.getListener();
+ this.source = s;
+ setTrigraphs(pp.getFeature(Feature.TRIGRAPHS),
+ pp.getWarning(Warning.TRIGRAPHS));
+ }
+
+ private int __read() throws IOException {
+ if (uptr > 0)
+ return unget[--uptr];
+ return in.read();
+ }
+
+ private void _unread(int c) {
+ if (c != -1)
+ unget[uptr++] = c;
+ assert uptr <= unget.length :
+ "JoinReader ungets too many characters";
+ }
+
+ protected void warning(String msg)
+ throws LexerException {
+ if (source != null)
+ source.warning(msg);
+ else
+ throw new LexerException(msg);
+ }
+
+ private char trigraph(char raw, char repl)
+ throws IOException, LexerException {
+ if (trigraphs) {
+ if (warnings)
+ warning("trigraph ??" + raw + " converted to " + repl);
+ return repl;
+ } else {
+ if (warnings)
+ warning("trigraph ??" + raw + " ignored");
+ _unread(raw);
+ _unread('?');
+ return '?';
+ }
+ }
+
+ private int _read()
+ throws IOException, LexerException {
+ int c = __read();
+ if (c == '?' && (trigraphs || warnings)) {
+ int d = __read();
+ if (d == '?') {
+ int e = __read();
+ switch (e) {
+ case '(':
+ return trigraph('(', '[');
+ case ')':
+ return trigraph(')', ']');
+ case '<':
+ return trigraph('<', '{');
+ case '>':
+ return trigraph('>', '}');
+ case '=':
+ return trigraph('=', '#');
+ case '/':
+ return trigraph('/', '\\');
+ case '\'':
+ return trigraph('\'', '^');
+ case '!':
+ return trigraph('!', '|');
+ case '-':
+ return trigraph('-', '~');
+ }
+ _unread(e);
+ }
+ _unread(d);
+ }
+ return c;
+ }
+
+ public int read()
+ throws IOException, LexerException {
+ if (flushnl) {
+ if (newlines > 0) {
+ newlines--;
+ return '\n';
+ }
+ flushnl = false;
+ }
+
+ for (;;) {
+ int c = _read();
+ switch (c) {
+ case '\\':
+ int d = _read();
+ switch (d) {
+ case '\n':
+ newlines++;
+ continue;
+ case '\r':
+ newlines++;
+ int e = _read();
+ if (e != '\n')
+ _unread(e);
+ continue;
+ default:
+ _unread(d);
+ return c;
+ }
+ case '\r':
+ case '\n':
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ flushnl = true;
+ return c;
+ case -1:
+ if (newlines > 0) {
+ newlines--;
+ return '\n';
+ }
+ default:
+ return c;
+ }
+ }
+ }
+
+ public int read(char cbuf[], int off, int len)
+ throws IOException, LexerException {
+ for (int i = 0; i < len; i++) {
+ int ch = read();
+ if (ch == -1)
+ return i;
+ cbuf[off + i] = (char) ch;
+ }
+ return len;
+ }
+
+ @Override
+ public void close()
+ throws IOException {
+ in.close();
+ }
+
+ @Override
+ public String toString() {
+ return "JoinReader(nl=" + newlines + ")";
+ }
+
+ /*
+ public static void main(String[] args) throws IOException {
+ FileReader f = new FileReader(new File(args[0]));
+ BufferedReader b = new BufferedReader(f);
+ JoinReader r = new JoinReader(b);
+ BufferedWriter w = new BufferedWriter(
+ new java.io.OutputStreamWriter(System.out)
+ );
+ int c;
+ while ((c = r.read()) != -1) {
+ w.write((char)c);
+ }
+ w.close();
+ }
+ */
+}
diff --git a/src/java/org/anarres/cpp/LexerException.java b/src/main/java/org/anarres/cpp/LexerException.java
index 41c6275..41c6275 100644
--- a/src/java/org/anarres/cpp/LexerException.java
+++ b/src/main/java/org/anarres/cpp/LexerException.java
diff --git a/src/main/java/org/anarres/cpp/LexerSource.java b/src/main/java/org/anarres/cpp/LexerSource.java
new file mode 100644
index 0000000..ca18314
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/LexerSource.java
@@ -0,0 +1,910 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.annotation.Nonnull;
+import static org.anarres.cpp.Token.*;
+
+/** Does not handle digraphs. */
+public class LexerSource extends Source {
+
+ private static final boolean DEBUG = false;
+
+ private JoinReader reader;
+ private final boolean ppvalid;
+ private boolean bol;
+ private boolean include;
+
+ private boolean digraphs;
+
+ /* Unread. */
+ private int u0, u1;
+ private int ucount;
+
+ private int line;
+ private int column;
+ private int lastcolumn;
+ private boolean cr;
+
+ /* ppvalid is:
+ * false in StringLexerSource,
+ * true in FileLexerSource */
+ public LexerSource(Reader r, boolean ppvalid) {
+ this.reader = new JoinReader(r);
+ this.ppvalid = ppvalid;
+ this.bol = true;
+ this.include = false;
+
+ this.digraphs = true;
+
+ this.ucount = 0;
+
+ this.line = 1;
+ this.column = 0;
+ this.lastcolumn = -1;
+ this.cr = false;
+ }
+
+ @Override
+ /* pp */ void init(Preprocessor pp) {
+ super.init(pp);
+ this.digraphs = pp.getFeature(Feature.DIGRAPHS);
+ this.reader.init(pp, this);
+ }
+
+ @Override
+ public int getLine() {
+ return line;
+ }
+
+ @Override
+ public int getColumn() {
+ return column;
+ }
+
+ @Override
+ /* pp */ boolean isNumbered() {
+ return true;
+ }
+
+ /* Error handling. */
+ private void _error(String msg, boolean error)
+ throws LexerException {
+ int _l = line;
+ int _c = column;
+ if (_c == 0) {
+ _c = lastcolumn;
+ _l--;
+ } else {
+ _c--;
+ }
+ if (error)
+ super.error(_l, _c, msg);
+ else
+ super.warning(_l, _c, msg);
+ }
+
+ /* Allow JoinReader to call this. */
+ /* pp */ final void error(String msg)
+ throws LexerException {
+ _error(msg, true);
+ }
+
+ /* Allow JoinReader to call this. */
+ /* pp */ final void warning(String msg)
+ throws LexerException {
+ _error(msg, false);
+ }
+
+ /* A flag for string handling. */
+
+ /* pp */ void setInclude(boolean b) {
+ this.include = b;
+ }
+
+ /*
+ private boolean _isLineSeparator(int c) {
+ return Character.getType(c) == Character.LINE_SEPARATOR
+ || c == -1;
+ }
+ */
+
+ /* XXX Move to JoinReader and canonicalise newlines. */
+ private static boolean isLineSeparator(int c) {
+ switch ((char) c) {
+ case '\r':
+ case '\n':
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ return true;
+ default:
+ return (c == -1);
+ }
+ }
+
+ private int read()
+ throws IOException,
+ LexerException {
+ int c;
+ assert ucount <= 2 : "Illegal ucount: " + ucount;
+ switch (ucount) {
+ case 2:
+ ucount = 1;
+ c = u1;
+ break;
+ case 1:
+ ucount = 0;
+ c = u0;
+ break;
+ default:
+ if (reader == null)
+ c = -1;
+ else
+ c = reader.read();
+ break;
+ }
+
+ switch (c) {
+ case '\r':
+ cr = true;
+ line++;
+ lastcolumn = column;
+ column = 0;
+ break;
+ case '\n':
+ if (cr) {
+ cr = false;
+ break;
+ }
+ /* fallthrough */
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ cr = false;
+ line++;
+ lastcolumn = column;
+ column = 0;
+ break;
+ case -1:
+ cr = false;
+ break;
+ default:
+ cr = false;
+ column++;
+ break;
+ }
+
+ /*
+ if (isLineSeparator(c)) {
+ line++;
+ lastcolumn = column;
+ column = 0;
+ }
+ else {
+ column++;
+ }
+ */
+ return c;
+ }
+
+ /* You can unget AT MOST one newline. */
+ private void unread(int c)
+ throws IOException {
+ /* XXX Must unread newlines. */
+ if (c != -1) {
+ if (isLineSeparator(c)) {
+ line--;
+ column = lastcolumn;
+ cr = false;
+ } else {
+ column--;
+ }
+ switch (ucount) {
+ case 0:
+ u0 = c;
+ ucount = 1;
+ break;
+ case 1:
+ u1 = c;
+ ucount = 2;
+ break;
+ default:
+ throw new IllegalStateException(
+ "Cannot unget another character!"
+ );
+ }
+ // reader.unread(c);
+ }
+ }
+
+ /* Consumes the rest of the current line into an invalid. */
+ @Nonnull
+ private Token invalid(StringBuilder text, String reason)
+ throws IOException,
+ LexerException {
+ int d = read();
+ while (!isLineSeparator(d)) {
+ text.append((char) d);
+ d = read();
+ }
+ unread(d);
+ return new Token(INVALID, 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 != '*');
+ do {
+ d = read();
+ text.append((char) d);
+ } while (d == '*');
+ } while (d != '/');
+ return new Token(CCOMMENT, text.toString());
+ }
+
+ @Nonnull
+ private Token cppcomment()
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder("//");
+ int d = read();
+ while (!isLineSeparator(d)) {
+ text.append((char) d);
+ d = read();
+ }
+ unread(d);
+ return new Token(CPPCOMMENT, text.toString());
+ }
+
+ private int escape(StringBuilder text)
+ throws IOException,
+ LexerException {
+ int d = read();
+ switch (d) {
+ case 'a':
+ text.append('a');
+ return 0x07;
+ case 'b':
+ text.append('b');
+ return '\b';
+ case 'f':
+ text.append('f');
+ return '\f';
+ case 'n':
+ text.append('n');
+ return '\n';
+ case 'r':
+ text.append('r');
+ return '\r';
+ case 't':
+ text.append('t');
+ return '\t';
+ case 'v':
+ text.append('v');
+ return 0x0b;
+ case '\\':
+ text.append('\\');
+ return '\\';
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ int len = 0;
+ int val = 0;
+ do {
+ val = (val << 3) + Character.digit(d, 8);
+ text.append((char) d);
+ d = read();
+ } while (++len < 3 && Character.digit(d, 8) != -1);
+ unread(d);
+ return val;
+
+ case 'x':
+ text.append((char) d);
+ len = 0;
+ val = 0;
+ while (len++ < 2) {
+ d = read();
+ if (Character.digit(d, 16) == -1) {
+ unread(d);
+ break;
+ }
+ val = (val << 4) + Character.digit(d, 16);
+ text.append((char) d);
+ }
+ return val;
+
+ /* Exclude two cases from the warning. */
+ case '"':
+ text.append('"');
+ return '"';
+ case '\'':
+ text.append('\'');
+ return '\'';
+
+ default:
+ warning("Unnecessary escape character " + (char) d);
+ text.append((char) d);
+ return d;
+ }
+ }
+
+ @Nonnull
+ private Token character()
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder("'");
+ int d = read();
+ if (d == '\\') {
+ text.append('\\');
+ d = escape(text);
+ } else if (isLineSeparator(d)) {
+ unread(d);
+ return new Token(INVALID, text.toString(),
+ "Unterminated character literal");
+ } else if (d == '\'') {
+ text.append('\'');
+ return new Token(INVALID, text.toString(),
+ "Empty character literal");
+ } else if (!Character.isDefined(d)) {
+ text.append('?');
+ return invalid(text, "Illegal unicode character literal");
+ } else {
+ text.append((char) d);
+ }
+
+ int e = read();
+ if (e != '\'') {
+ // error("Illegal character constant");
+ /* We consume up to the next ' or the rest of the line. */
+ for (;;) {
+ if (isLineSeparator(e)) {
+ unread(e);
+ break;
+ }
+ text.append((char) e);
+ if (e == '\'')
+ break;
+ e = read();
+ }
+ return new Token(INVALID, text.toString(),
+ "Illegal character constant " + text);
+ }
+ text.append('\'');
+ /* XXX It this a bad cast? */
+ return new Token(CHARACTER,
+ text.toString(), Character.valueOf((char) d));
+ }
+
+ @Nonnull
+ private Token string(char open, char close)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ text.append(open);
+
+ StringBuilder buf = new StringBuilder();
+
+ for (;;) {
+ int c = read();
+ if (c == close) {
+ break;
+ } else if (c == '\\') {
+ text.append('\\');
+ if (!include) {
+ char d = (char) escape(text);
+ buf.append(d);
+ }
+ } 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);
+ } else if (isLineSeparator(c)) {
+ unread(c);
+ // error("Unterminated string literal after " + buf);
+ return new Token(INVALID, text.toString(),
+ "Unterminated string literal after " + buf);
+ } else {
+ text.append((char) c);
+ buf.append((char) c);
+ }
+ }
+ text.append(close);
+ switch (close) {
+ case '"':
+ return new Token(STRING,
+ text.toString(), buf.toString());
+ case '>':
+ return new Token(HEADER,
+ text.toString(), buf.toString());
+ case '\'':
+ if (buf.length() == 1)
+ return new Token(CHARACTER,
+ text.toString(), buf.toString());
+ return new Token(SQSTRING,
+ text.toString(), buf.toString());
+ default:
+ throw new IllegalStateException(
+ "Unknown closing character " + String.valueOf(close));
+ }
+ }
+
+ @Nonnull
+ private Token _number_suffix(StringBuilder text, NumericValue value, int d)
+ throws IOException,
+ LexerException {
+ int flags = 0; // U, I, L, LL, F, D, MSB
+ for (;;) {
+ if (d == 'U' || d == 'u') {
+ if ((flags & NumericValue.F_UNSIGNED) != 0)
+ warning("Duplicate unsigned suffix " + d);
+ flags |= NumericValue.F_UNSIGNED;
+ text.append((char) d);
+ d = read();
+ } else if (d == 'L' || d == 'l') {
+ if ((flags & NumericValue.FF_SIZE) != 0)
+ warning("Nultiple length suffixes after " + text);
+ text.append((char) d);
+ int e = read();
+ if (e == d) { // Case must match. Ll is Welsh.
+ flags |= NumericValue.F_LONGLONG;
+ text.append((char) e);
+ d = read();
+ } else {
+ flags |= NumericValue.F_LONG;
+ d = e;
+ }
+ } else if (d == 'I' || d == 'i') {
+ if ((flags & NumericValue.FF_SIZE) != 0)
+ warning("Nultiple 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);
+ 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);
+ flags |= NumericValue.F_DOUBLE;
+ text.append((char) d);
+ d = read();
+ } // This should probably be isPunct() || isWhite().
+ else if (Character.isLetter(d) || d == '_') {
+ unread(d);
+ value.setFlags(flags);
+ return invalid(text,
+ "Invalid suffix \"" + (char) d
+ + "\" on numeric constant");
+ } else {
+ unread(d);
+ value.setFlags(flags);
+ return new Token(NUMBER,
+ text.toString(), value);
+ }
+ }
+ }
+
+ /* Either a decimal part, or a hex exponent. */
+ @Nonnull
+ private String _number_part(StringBuilder text, int base)
+ throws IOException,
+ LexerException {
+ StringBuilder part = new StringBuilder();
+ int d = read();
+ while (Character.digit(d, base) != -1) {
+ text.append((char) d);
+ part.append((char) d);
+ d = read();
+ }
+ unread(d);
+ 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)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder("0");
+ text.append(x);
+ String integer = _number_part(text, 16);
+ NumericValue value = new NumericValue(16, integer);
+ int d = read();
+ if (d == '.') {
+ String fraction = _number_part(text, 16);
+ value.setFractionalPart(fraction);
+ d = read();
+ }
+ if (d == 'P' || d == 'p') {
+ String exponent = _number_part(text, 10);
+ value.setExponent(exponent);
+ d = read();
+ }
+ // XXX Make sure it's got enough parts
+ return _number_suffix(text, value, d);
+ }
+
+ /* We know we have at least one valid digit, but empty is not
+ * fine. */
+ @Nonnull
+ private Token number_decimal()
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ String integer = _number_part(text, 10);
+ NumericValue value = new NumericValue(10, integer);
+ int d = read();
+ if (d == '.') {
+ String fraction = _number_part(text, 10);
+ value.setFractionalPart(fraction);
+ d = read();
+ }
+ if (d == 'E' || d == 'e') {
+ String exponent = _number_part(text, 10);
+ value.setExponent(exponent);
+ d = read();
+ }
+ // XXX Make sure it's got enough parts
+ return _number_suffix(text, value, d);
+ }
+
+ @Nonnull
+ private Token identifier(int c)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ int d;
+ text.append((char) c);
+ for (;;) {
+ d = read();
+ if (Character.isIdentifierIgnorable(d))
+ ; else if (Character.isJavaIdentifierPart(d))
+ text.append((char) d);
+ else
+ break;
+ }
+ unread(d);
+ return new Token(IDENTIFIER, text.toString());
+ }
+
+ @Nonnull
+ private Token whitespace(int c)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ int d;
+ text.append((char) c);
+ for (;;) {
+ d = read();
+ if (ppvalid && isLineSeparator(d)) /* XXX Ugly. */
+
+ break;
+ if (Character.isWhitespace(d))
+ text.append((char) d);
+ else
+ break;
+ }
+ unread(d);
+ return new Token(WHITESPACE, text.toString());
+ }
+
+ /* No token processed by cond() contains a newline. */
+ @Nonnull
+ private Token cond(char c, int yes, int no)
+ throws IOException,
+ LexerException {
+ int d = read();
+ if (c == d)
+ return new Token(yes);
+ unread(d);
+ return new Token(no);
+ }
+
+ @Override
+ public Token token()
+ throws IOException,
+ LexerException {
+ Token tok = null;
+
+ int _l = line;
+ int _c = column;
+
+ int c = read();
+ int d;
+
+ switch (c) {
+ case '\n':
+ if (ppvalid) {
+ bol = true;
+ if (include) {
+ tok = new Token(NL, _l, _c, "\n");
+ } else {
+ int nls = 0;
+ do {
+ nls++;
+ d = read();
+ } while (d == '\n');
+ unread(d);
+ char[] text = new char[nls];
+ for (int i = 0; i < text.length; i++)
+ text[i] = '\n';
+ // Skip the bol = false below.
+ tok = new Token(NL, _l, _c, new String(text));
+ }
+ if (DEBUG)
+ System.out.println("lx: Returning NL: " + tok);
+ return tok;
+ }
+ /* Let it be handled as whitespace. */
+ break;
+
+ case '!':
+ tok = cond('=', NE, '!');
+ break;
+
+ case '#':
+ if (bol)
+ tok = new Token(HASH);
+ else
+ tok = cond('#', PASTE, '#');
+ break;
+
+ case '+':
+ d = read();
+ if (d == '+')
+ tok = new Token(INC);
+ else if (d == '=')
+ tok = new Token(PLUS_EQ);
+ else
+ unread(d);
+ break;
+ case '-':
+ d = read();
+ if (d == '-')
+ tok = new Token(DEC);
+ else if (d == '=')
+ tok = new Token(SUB_EQ);
+ else if (d == '>')
+ tok = new Token(ARROW);
+ else
+ unread(d);
+ break;
+
+ case '*':
+ tok = cond('=', MULT_EQ, '*');
+ break;
+ case '/':
+ d = read();
+ if (d == '*')
+ tok = ccomment();
+ else if (d == '/')
+ tok = cppcomment();
+ else if (d == '=')
+ tok = new Token(DIV_EQ);
+ else
+ unread(d);
+ break;
+
+ case '%':
+ d = read();
+ if (d == '=')
+ tok = new Token(MOD_EQ);
+ else if (digraphs && d == '>')
+ tok = new Token('}'); // digraph
+ else if (digraphs && d == ':')
+ PASTE:
+ {
+ d = read();
+ if (d != '%') {
+ unread(d);
+ tok = new Token('#'); // digraph
+ break PASTE;
+ }
+ d = read();
+ if (d != ':') {
+ unread(d); // Unread 2 chars here.
+ unread('%');
+ tok = new Token('#'); // digraph
+ break PASTE;
+ }
+ tok = new Token(PASTE); // digraph
+ }
+ else
+ unread(d);
+ break;
+
+ case ':':
+ /* :: */
+ d = read();
+ if (digraphs && d == '>')
+ tok = new Token(']'); // digraph
+ else
+ unread(d);
+ break;
+
+ case '<':
+ if (include) {
+ tok = string('<', '>');
+ } else {
+ d = read();
+ if (d == '=')
+ tok = new Token(LE);
+ else if (d == '<')
+ tok = cond('=', LSH_EQ, LSH);
+ else if (digraphs && d == ':')
+ tok = new Token('['); // digraph
+ else if (digraphs && d == '%')
+ tok = new Token('{'); // digraph
+ else
+ unread(d);
+ }
+ break;
+
+ case '=':
+ tok = cond('=', EQ, '=');
+ break;
+
+ case '>':
+ d = read();
+ if (d == '=')
+ tok = new Token(GE);
+ else if (d == '>')
+ tok = cond('=', RSH_EQ, RSH);
+ else
+ unread(d);
+ break;
+
+ case '^':
+ tok = cond('=', XOR_EQ, '^');
+ break;
+
+ case '|':
+ d = read();
+ if (d == '=')
+ tok = new Token(OR_EQ);
+ else if (d == '|')
+ tok = cond('=', LOR_EQ, LOR);
+ else
+ unread(d);
+ break;
+ case '&':
+ d = read();
+ if (d == '&')
+ tok = cond('=', LAND_EQ, LAND);
+ else if (d == '=')
+ tok = new Token(AND_EQ);
+ else
+ unread(d);
+ break;
+
+ case '.':
+ d = read();
+ if (d == '.')
+ tok = cond('.', ELLIPSIS, RANGE);
+ else
+ unread(d);
+ if (Character.isDigit(d)) {
+ unread('.');
+ tok = number_decimal();
+ }
+ /* XXX decimal fraction */
+ break;
+
+ case '0':
+ /* octal or hex */
+ d = read();
+ if (d == 'x' || d == 'X')
+ tok = number_hex((char) d);
+ else {
+ unread(d);
+ tok = number_octal();
+ }
+ break;
+
+ case '\'':
+ tok = string('\'', '\'');
+ break;
+
+ case '"':
+ tok = string('"', '"');
+ break;
+
+ case -1:
+ close();
+ tok = new Token(EOF, _l, _c, "<eof>");
+ break;
+ }
+
+ if (tok == null) {
+ if (Character.isWhitespace(c)) {
+ tok = whitespace(c);
+ } else if (Character.isDigit(c)) {
+ unread(c);
+ tok = number_decimal();
+ } else if (Character.isJavaIdentifierStart(c)) {
+ tok = identifier(c);
+ } else {
+ tok = new Token(c);
+ }
+ }
+
+ if (bol) {
+ switch (tok.getType()) {
+ case WHITESPACE:
+ case CCOMMENT:
+ break;
+ default:
+ bol = false;
+ break;
+ }
+ }
+
+ tok.setLocation(_l, _c);
+ if (DEBUG)
+ System.out.println("lx: Returning " + tok);
+ // (new Exception("here")).printStackTrace(System.out);
+ return tok;
+ }
+
+ public void close()
+ throws IOException {
+ if (reader != null) {
+ reader.close();
+ reader = null;
+ }
+ super.close();
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/Macro.java b/src/main/java/org/anarres/cpp/Macro.java
index ca024bc..534cb2b 100644
--- a/src/java/org/anarres/cpp/Macro.java
+++ b/src/main/java/org/anarres/cpp/Macro.java
@@ -165,6 +165,7 @@ public class Macro {
return buf.toString();
}
+ @Override
public String toString() {
StringBuilder buf = new StringBuilder(name);
if (args != null) {
diff --git a/src/main/java/org/anarres/cpp/MacroTokenSource.java b/src/main/java/org/anarres/cpp/MacroTokenSource.java
new file mode 100644
index 0000000..516e4f2
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/MacroTokenSource.java
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.anarres.cpp.Token.*;
+
+/* This source should always be active, since we don't expand macros
+ * in any inactive context. */
+/* pp */ class MacroTokenSource extends Source {
+
+ private final Macro macro;
+ private final Iterator<Token> tokens; /* Pointer into the macro. */
+
+ private final List<Argument> args; /* { unexpanded, expanded } */
+
+ private Iterator<Token> arg; /* "current expansion" */
+
+ /* pp */ MacroTokenSource(Macro m, List<Argument> args) {
+ this.macro = m;
+ this.tokens = m.getTokens().iterator();
+ this.args = args;
+ this.arg = null;
+ }
+
+ @Override
+ /* pp */ boolean isExpanding(Macro m) {
+ /* When we are expanding an arg, 'this' macro is not
+ * being expanded, and thus we may re-expand it. */
+ if (/* XXX this.arg == null && */this.macro == m)
+ return true;
+ return super.isExpanding(m);
+ }
+
+ /* XXX Called from Preprocessor [ugly]. */
+ /* pp */ static void escape(StringBuilder buf, CharSequence cs) {
+ for (int i = 0; i < cs.length(); i++) {
+ char c = cs.charAt(i);
+ switch (c) {
+ case '\\':
+ buf.append("\\\\");
+ break;
+ case '"':
+ buf.append("\\\"");
+ break;
+ case '\n':
+ buf.append("\\n");
+ break;
+ case '\r':
+ buf.append("\\r");
+ break;
+ default:
+ buf.append(c);
+ }
+ }
+ }
+
+ private void concat(StringBuilder buf, Argument arg) {
+ Iterator<Token> it = arg.iterator();
+ while (it.hasNext()) {
+ Token tok = it.next();
+ buf.append(tok.getText());
+ }
+ }
+
+ private Token stringify(Token pos, Argument arg) {
+ StringBuilder buf = new StringBuilder();
+ concat(buf, arg);
+ // System.out.println("Concat: " + arg + " -> " + buf);
+ StringBuilder str = new StringBuilder("\"");
+ escape(str, buf);
+ str.append("\"");
+ // System.out.println("Escape: " + buf + " -> " + str);
+ return new Token(STRING,
+ pos.getLine(), pos.getColumn(),
+ str.toString(), buf.toString());
+ }
+
+
+ /* At this point, we have consumed the first M_PASTE.
+ * @see Macro#addPaste(Token) */
+ private void paste(Token ptok)
+ throws IOException,
+ LexerException {
+ StringBuilder buf = new StringBuilder();
+ Token err = null;
+ /* We know here that arg is null or expired,
+ * since we cannot paste an expanded arg. */
+
+ int count = 2;
+ for (int i = 0; i < count; i++) {
+ if (!tokens.hasNext()) {
+ /* XXX This one really should throw. */
+ error(ptok.getLine(), ptok.getColumn(),
+ "Paste at end of expansion");
+ buf.append(' ').append(ptok.getText());
+ break;
+ }
+ Token tok = tokens.next();
+ // System.out.println("Paste " + tok);
+ switch (tok.getType()) {
+ case M_PASTE:
+ /* One extra to paste, plus one because the
+ * paste token didn't count. */
+ count += 2;
+ ptok = tok;
+ break;
+ case M_ARG:
+ int idx = ((Integer) tok.getValue()).intValue();
+ concat(buf, args.get(idx));
+ break;
+ /* XXX Test this. */
+ case CCOMMENT:
+ case CPPCOMMENT:
+ break;
+ default:
+ buf.append(tok.getText());
+ break;
+ }
+ }
+
+ /* Push and re-lex. */
+ /*
+ StringBuilder src = new StringBuilder();
+ escape(src, buf);
+ StringLexerSource sl = new StringLexerSource(src.toString());
+ */
+ StringLexerSource sl = new StringLexerSource(buf.toString());
+
+ /* XXX Check that concatenation produces a valid token. */
+ arg = new SourceIterator(sl);
+ }
+
+ public Token token()
+ throws IOException,
+ LexerException {
+ for (;;) {
+ /* Deal with lexed tokens first. */
+
+ if (arg != null) {
+ if (arg.hasNext()) {
+ Token tok = arg.next();
+ /* XXX PASTE -> INVALID. */
+ assert tok.getType() != M_PASTE :
+ "Unexpected paste token";
+ return tok;
+ }
+ arg = null;
+ }
+
+ if (!tokens.hasNext())
+ return new Token(EOF, -1, -1, ""); /* End of macro. */
+
+ Token tok = tokens.next();
+ int idx;
+ switch (tok.getType()) {
+ case M_STRING:
+ /* Use the nonexpanded arg. */
+ idx = ((Integer) tok.getValue()).intValue();
+ return stringify(tok, args.get(idx));
+ case M_ARG:
+ /* Expand the arg. */
+ idx = ((Integer) tok.getValue()).intValue();
+ // System.out.println("Pushing arg " + args.get(idx));
+ arg = args.get(idx).expansion();
+ break;
+ case M_PASTE:
+ paste(tok);
+ break;
+ default:
+ return tok;
+ }
+ } /* for */
+
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("expansion of ").append(macro.getName());
+ Source parent = getParent();
+ if (parent != null)
+ buf.append(" in ").append(String.valueOf(parent));
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/org/anarres/cpp/Main.java b/src/main/java/org/anarres/cpp/Main.java
new file mode 100644
index 0000000..1902798
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/Main.java
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.EnumSet;
+import gnu.getopt.Getopt;
+import gnu.getopt.LongOpt;
+
+/**
+ * (Currently a simple test class).
+ */
+public class Main {
+
+ private static class Option extends LongOpt {
+
+ private String eg;
+ private String help;
+
+ public Option(String word, int arg, int ch,
+ String eg, String help) {
+ super(word, arg, null, ch);
+ this.eg = eg;
+ this.help = help;
+ }
+ }
+
+ private static final Option[] OPTS = new Option[]{
+ new Option("help", LongOpt.NO_ARGUMENT, 'h', null,
+ "Displays help and usage information."),
+ new Option("define", LongOpt.REQUIRED_ARGUMENT, 'D', "name=definition",
+ "Defines the given macro."),
+ new Option("undefine", LongOpt.REQUIRED_ARGUMENT, 'U', "name",
+ "Undefines the given macro, previously either builtin or defined using -D."),
+ new Option("include", LongOpt.REQUIRED_ARGUMENT, 1, "file",
+ "Process file as if \"#" + "include \"file\"\" appeared as the first line of the primary source file."),
+ new Option("incdir", LongOpt.REQUIRED_ARGUMENT, 'I', "dir",
+ "Adds the directory dir to the list of directories to be searched for header files."),
+ new Option("iquote", LongOpt.REQUIRED_ARGUMENT, 0, "dir",
+ "Adds the directory dir to the list of directories to be searched for header files included using \"\"."),
+ new Option("warning", LongOpt.REQUIRED_ARGUMENT, 'W', "type",
+ "Enables the named warning class (" + getWarnings() + ")."),
+ new Option("no-warnings", LongOpt.NO_ARGUMENT, 'w', null,
+ "Disables ALL warnings."),
+ new Option("verbose", LongOpt.NO_ARGUMENT, 'v', null,
+ "Operates incredibly verbosely."),
+ new Option("debug", LongOpt.NO_ARGUMENT, 3, null,
+ "Operates incredibly verbosely."),
+ new Option("version", LongOpt.NO_ARGUMENT, 2, null,
+ "Prints jcpp's version number (" + Version.getVersion() + ")"),};
+
+ private static CharSequence getWarnings() {
+ StringBuilder buf = new StringBuilder();
+ for (Warning w : Warning.values()) {
+ if (buf.length() > 0)
+ buf.append(", ");
+ String name = w.name().toLowerCase();
+ buf.append(name.replace('_', '-'));
+ }
+ return buf;
+ }
+
+ public static void main(String[] args) throws Exception {
+ (new Main()).run(args);
+ }
+
+ public void run(String[] args) throws Exception {
+ Option[] opts = OPTS;
+ String sopts = getShortOpts(opts);
+ Getopt g = new Getopt("jcpp", args, sopts, opts);
+ int c;
+ String arg;
+ int idx;
+
+ Preprocessor pp = new Preprocessor();
+ pp.addFeature(Feature.DIGRAPHS);
+ pp.addFeature(Feature.TRIGRAPHS);
+ pp.addFeature(Feature.LINEMARKERS);
+ pp.addWarning(Warning.IMPORT);
+ pp.setListener(new PreprocessorListener());
+ pp.addMacro("__JCPP__");
+ pp.getSystemIncludePath().add("/usr/local/include");
+ pp.getSystemIncludePath().add("/usr/include");
+ pp.getFrameworksPath().add("/System/Library/Frameworks");
+ pp.getFrameworksPath().add("/Library/Frameworks");
+ pp.getFrameworksPath().add("/Local/Library/Frameworks");
+
+ GETOPT:
+ while ((c = g.getopt()) != -1) {
+ switch (c) {
+ case 'D':
+ arg = g.getOptarg();
+ idx = arg.indexOf('=');
+ if (idx == -1)
+ pp.addMacro(arg);
+ else
+ pp.addMacro(arg.substring(0, idx),
+ arg.substring(idx + 1));
+ break;
+ case 'U':
+ pp.getMacros().remove(g.getOptarg());
+ break;
+ case 'I':
+ pp.getSystemIncludePath().add(g.getOptarg());
+ break;
+ case 0: // --iquote=
+ pp.getQuoteIncludePath().add(g.getOptarg());
+ break;
+ case 'W':
+ arg = g.getOptarg().toUpperCase();
+ arg = arg.replace('-', '_');
+ if (arg.equals("ALL"))
+ pp.addWarnings(EnumSet.allOf(Warning.class));
+ else
+ pp.addWarning(Enum.valueOf(Warning.class, arg));
+ break;
+ case 'w':
+ pp.getWarnings().clear();
+ break;
+ case 1: // --include=
+ // pp.addInput(new File(g.getOptarg()));
+ // Comply exactly with spec.
+ pp.addInput(new StringLexerSource(
+ "#" + "include \"" + g.getOptarg() + "\"\n"
+ ));
+ break;
+ case 2: // --version
+ version(System.out);
+ return;
+ case 'v':
+ pp.addFeature(Feature.VERBOSE);
+ break;
+ case 3:
+ pp.addFeature(Feature.DEBUG);
+ break;
+ case 'h':
+ usage(getClass().getName(), opts);
+ return;
+ default:
+ throw new Exception("Illegal option " + (char) c);
+ case '?':
+ continue; /* Make failure-proof. */
+
+ }
+ }
+
+ for (int i = g.getOptind(); i < args.length; i++)
+ pp.addInput(new FileLexerSource(new File(args[i])));
+ if (g.getOptind() == args.length)
+ pp.addInput(new InputLexerSource(System.in));
+
+ if (pp.getFeature(Feature.VERBOSE)) {
+ System.err.println("#" + "include \"...\" search starts here:");
+ for (String dir : pp.getQuoteIncludePath())
+ System.err.println(" " + dir);
+ System.err.println("#" + "include <...> search starts here:");
+ for (String dir : pp.getSystemIncludePath())
+ System.err.println(" " + dir);
+ System.err.println("End of search list.");
+ }
+
+ try {
+ for (;;) {
+ Token tok = pp.token();
+ if (tok == null)
+ break;
+ if (tok.getType() == Token.EOF)
+ break;
+ System.out.print(tok.getText());
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ Source s = pp.getSource();
+ while (s != null) {
+ System.err.println(" -> " + s);
+ s = s.getParent();
+ }
+ }
+
+ }
+
+ private void version(PrintStream out) {
+ out.println("Anarres Java C Preprocessor version " + Version.getVersion());
+ out.println("Copyright (C) 2008-2014 Shevek (http://www.anarres.org/).");
+ out.println("This is free software; see the source for copying conditions. There is NO");
+ out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
+ }
+
+ private static String getShortOpts(Option[] opts)
+ throws Exception {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < opts.length; i++) {
+ char c = (char) opts[i].getVal();
+ if (!Character.isLetterOrDigit(c))
+ continue;
+ for (int j = 0; j < buf.length(); j++)
+ if (buf.charAt(j) == c)
+ throw new Exception(
+ "Duplicate short option " + c
+ );
+ buf.append(c);
+ switch (opts[i].getHasArg()) {
+ case LongOpt.NO_ARGUMENT:
+ break;
+ case LongOpt.OPTIONAL_ARGUMENT:
+ buf.append("::");
+ break;
+ case LongOpt.REQUIRED_ARGUMENT:
+ buf.append(":");
+ break;
+ }
+ }
+ return buf.toString();
+ }
+
+ /* This is incomplete but nearly there. */
+ /**
+ * Wraps a string.
+ *
+ * The output form is:
+ * <pre>
+ * prefix in[0]
+ * &lt;--indent-&gt; in[1]
+ * &lt;--indent-&gt; in[2]
+ * &lt;-----width----&gt;
+ * </pre>
+ */
+ /* XXX There's some of this in commons. */
+ private static String wrap(String in, String prefix,
+ int indent, int width) {
+ StringBuilder buf = new StringBuilder(prefix);
+
+ while (buf.length() < indent)
+ buf.append(' ');
+
+ int start = 0;
+
+ while (start < in.length()) {
+ while (start < in.length()
+ && Character.isWhitespace(in.charAt(start)))
+ start++;
+
+ int end = start + width - indent;
+
+ if (end > in.length()) {
+ buf.append(in.substring(start));
+ break;
+ }
+
+ int idx = end;
+ while (!Character.isWhitespace(in.charAt(idx)))
+ idx--;
+
+ if (idx == start) {
+ idx = end - 1;
+ buf.append(in.substring(start, idx));
+ buf.append('-');
+ } else {
+ buf.append(in.substring(start, idx));
+ start = idx;
+ }
+
+ start = idx;
+ }
+
+ return buf.toString();
+ }
+
+ private static void usage(String command, Option[] options) {
+ StringBuilder text = new StringBuilder("Usage: ");
+ text.append(command).append('\n');
+ for (int i = 0; i < options.length; i++) {
+ StringBuilder line = new StringBuilder();
+ Option opt = options[i];
+ line.append(" --").append(opt.getName());
+ switch (opt.getHasArg()) {
+ case LongOpt.NO_ARGUMENT:
+ break;
+ case LongOpt.OPTIONAL_ARGUMENT:
+ line.append("[=").append(opt.eg).append(']');
+ break;
+ case LongOpt.REQUIRED_ARGUMENT:
+ line.append('=').append(opt.eg);
+ break;
+ }
+ if (Character.isLetterOrDigit(opt.getVal()))
+ line.append(" (-").append((char) opt.getVal()).append(")");
+ if (line.length() < 30) {
+ while (line.length() < 30)
+ line.append(' ');
+ } else {
+ line.append('\n');
+ for (int j = 0; j < 30; j++)
+ line.append(' ');
+ }
+ /* This should use wrap. */
+ line.append(opt.help);
+ line.append('\n');
+ text.append(line);
+ }
+
+ System.out.println(text);
+ }
+
+}
diff --git a/src/main/java/org/anarres/cpp/NumericValue.java b/src/main/java/org/anarres/cpp/NumericValue.java
new file mode 100644
index 0000000..8d961c4
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/NumericValue.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class NumericValue extends Number {
+
+ public static final int F_UNSIGNED = 1;
+ public static final int F_INT = 2;
+ public static final int F_LONG = 4;
+ public static final int F_LONGLONG = 8;
+ public static final int F_FLOAT = 16;
+ public static final int F_DOUBLE = 32;
+
+ public static final int FF_SIZE = F_INT | F_LONG | F_LONGLONG | F_FLOAT | F_DOUBLE;
+
+ private final int base;
+ private final String integer;
+ private String fraction;
+ private String exponent;
+ private int flags;
+
+ public NumericValue(int base, String integer) {
+ this.base = base;
+ this.integer = integer;
+ }
+
+ public int getBase() {
+ return base;
+ }
+
+ public String getIntegerPart() {
+ return integer;
+ }
+
+ public String getFractionalPart() {
+ return fraction;
+ }
+
+ /* pp */ void setFractionalPart(String fraction) {
+ this.fraction = fraction;
+ }
+
+ public String getExponent() {
+ return exponent;
+ }
+
+ /* pp */ void setExponent(String exponent) {
+ this.exponent = exponent;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ /* pp */ void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * So, it turns out that parsing arbitrary bases into arbitrary
+ * precision numbers is nontrivial, and this routine gets it wrong
+ * in many important cases.
+ */
+ public BigDecimal toBigDecimal() {
+ int scale = 0;
+ String text = getIntegerPart();
+ String t_fraction = getFractionalPart();
+ if (t_fraction != null) {
+ text += getFractionalPart();
+ // XXX Wrong for anything but base 10.
+ scale += getFractionalPart().length();
+ }
+ if (getExponent() != null)
+ scale -= Integer.parseInt(getExponent());
+ BigInteger unscaled = new BigInteger(text, getBase());
+ return new BigDecimal(unscaled, scale);
+ }
+
+ public Number toJavaLangNumber() {
+ int flags = getFlags();
+ if ((flags & F_DOUBLE) != 0)
+ return doubleValue();
+ else if ((flags & F_FLOAT) != 0)
+ return floatValue();
+ else if ((flags & (F_LONG | F_LONGLONG)) != 0)
+ return longValue();
+ else if ((flags & F_INT) != 0)
+ return intValue();
+ else if (getFractionalPart() != null)
+ return doubleValue(); // .1 is a double in Java.
+ else if (getExponent() != null)
+ return doubleValue();
+ else
+ return intValue();
+ }
+
+ @Override
+ public int intValue() {
+ return Integer.parseInt(toString());
+ }
+
+ @Override
+ public long longValue() {
+ return Long.parseLong(toString());
+ }
+
+ @Override
+ public float floatValue() {
+ return Float.parseFloat(toString());
+ }
+
+ @Override
+ public double doubleValue() {
+ return Double.parseDouble(toString());
+ }
+
+ private boolean appendFlags(StringBuilder buf, String suffix, int flag) {
+ if ((getFlags() & flag) != flag)
+ return false;
+ buf.append(suffix);
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ switch (base) {
+ case 8:
+ buf.append('0');
+ break;
+ case 10:
+ break;
+ case 16:
+ buf.append("0x");
+ break;
+ case 2:
+ buf.append('b');
+ break;
+ default:
+ buf.append("[base-").append(base).append("]");
+ break;
+ }
+ buf.append(getIntegerPart());
+ if (getFractionalPart() != null)
+ buf.append('.').append(getFractionalPart());
+ if (getExponent() != null) {
+ buf.append(base > 10 ? 'p' : 'e');
+ buf.append(getExponent());
+ }
+ /*
+ if (appendFlags(buf, "ui", F_UNSIGNED | F_INT));
+ else if (appendFlags(buf, "ul", F_UNSIGNED | F_LONG));
+ else if (appendFlags(buf, "ull", F_UNSIGNED | F_LONGLONG));
+ else if (appendFlags(buf, "i", F_INT));
+ else if (appendFlags(buf, "l", F_LONG));
+ else if (appendFlags(buf, "ll", F_LONGLONG));
+ else if (appendFlags(buf, "f", F_FLOAT));
+ else if (appendFlags(buf, "d", F_DOUBLE));
+ */
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/org/anarres/cpp/Preprocessor.java b/src/main/java/org/anarres/cpp/Preprocessor.java
new file mode 100644
index 0000000..35480b2
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/Preprocessor.java
@@ -0,0 +1,2016 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import javax.annotation.Nonnull;
+import static org.anarres.cpp.Token.*;
+import static org.anarres.cpp.PreprocessorCommand.*;
+
+/**
+ * A C Preprocessor.
+ * The Preprocessor outputs a token stream which does not need
+ * re-lexing for C or C++. Alternatively, the output text may be
+ * reconstructed by concatenating the {@link Token#getText() text}
+ * values of the returned {@link Token Tokens}. (See
+ * {@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.
+ */
+public class Preprocessor implements Closeable {
+
+ private static final Source INTERNAL = new Source() {
+ @Override
+ public Token token()
+ throws IOException,
+ LexerException {
+ throw new LexerException("Cannot read from " + getName());
+ }
+
+ @Override
+ public String getPath() {
+ return "<internal-data>";
+ }
+
+ @Override
+ public String getName() {
+ return "internal data";
+ }
+ };
+ private static final Macro __LINE__ = new Macro(INTERNAL, "__LINE__");
+ private static final Macro __FILE__ = new Macro(INTERNAL, "__FILE__");
+ private static final Macro __COUNTER__ = new Macro(INTERNAL, "__COUNTER__");
+
+ private List<Source> inputs;
+
+ /* The fundamental engine. */
+ private Map<String, Macro> macros;
+ private Stack<State> states;
+ private Source source;
+
+ /* Miscellaneous support. */
+ private int counter;
+
+ /* Support junk to make it work like cpp */
+ private List<String> quoteincludepath; /* -iquote */
+
+ private List<String> sysincludepath; /* -I */
+
+ private List<String> frameworkspath;
+ private Set<Feature> features;
+ private Set<Warning> warnings;
+ private VirtualFileSystem filesystem;
+ private PreprocessorListener listener;
+
+ public Preprocessor() {
+ this.inputs = new ArrayList<Source>();
+
+ this.macros = new HashMap<String, Macro>();
+ macros.put(__LINE__.getName(), __LINE__);
+ macros.put(__FILE__.getName(), __FILE__);
+ macros.put(__COUNTER__.getName(), __COUNTER__);
+ this.states = new Stack<State>();
+ states.push(new State());
+ this.source = null;
+
+ this.counter = 0;
+
+ this.quoteincludepath = new ArrayList<String>();
+ this.sysincludepath = new ArrayList<String>();
+ this.frameworkspath = new ArrayList<String>();
+ this.features = EnumSet.noneOf(Feature.class);
+ this.warnings = EnumSet.noneOf(Warning.class);
+ this.filesystem = new JavaFileSystem();
+ this.listener = null;
+ }
+
+ public Preprocessor(Source initial) {
+ this();
+ addInput(initial);
+ }
+
+ /** Equivalent to
+ * 'new Preprocessor(new {@link FileLexerSource}(file))'
+ */
+ public Preprocessor(File file)
+ throws IOException {
+ this(new FileLexerSource(file));
+ }
+
+ /**
+ * Sets the VirtualFileSystem used by this Preprocessor.
+ */
+ public void setFileSystem(VirtualFileSystem filesystem) {
+ this.filesystem = filesystem;
+ }
+
+ /**
+ * Returns the VirtualFileSystem used by this Preprocessor.
+ */
+ public VirtualFileSystem getFileSystem() {
+ return filesystem;
+ }
+
+ /**
+ * Sets the PreprocessorListener which handles events for
+ * this Preprocessor.
+ *
+ * The listener is notified of warnings, errors and source
+ * changes, amongst other things.
+ */
+ public void setListener(PreprocessorListener listener) {
+ this.listener = listener;
+ Source s = source;
+ while (s != null) {
+ // s.setListener(listener);
+ s.init(this);
+ s = s.getParent();
+ }
+ }
+
+ /**
+ * Returns the PreprocessorListener which handles events for
+ * this Preprocessor.
+ */
+ public PreprocessorListener getListener() {
+ return listener;
+ }
+
+ /**
+ * Returns the feature-set for this Preprocessor.
+ *
+ * This set may be freely modified by user code.
+ */
+ public Set<Feature> getFeatures() {
+ return features;
+ }
+
+ /**
+ * Adds a feature to the feature-set of this Preprocessor.
+ */
+ public void addFeature(Feature f) {
+ features.add(f);
+ }
+
+ /**
+ * Adds features to the feature-set of this Preprocessor.
+ */
+ public void addFeatures(Collection<Feature> f) {
+ features.addAll(f);
+ }
+
+ /**
+ * Returns true if the given feature is in
+ * the feature-set of this Preprocessor.
+ */
+ public boolean getFeature(Feature f) {
+ return features.contains(f);
+ }
+
+ /**
+ * Returns the warning-set for this Preprocessor.
+ *
+ * This set may be freely modified by user code.
+ */
+ public Set<Warning> getWarnings() {
+ return warnings;
+ }
+
+ /**
+ * Adds a warning to the warning-set of this Preprocessor.
+ */
+ public void addWarning(Warning w) {
+ warnings.add(w);
+ }
+
+ /**
+ * Adds warnings to the warning-set of this Preprocessor.
+ */
+ public void addWarnings(Collection<Warning> w) {
+ warnings.addAll(w);
+ }
+
+ /**
+ * Returns true if the given warning is in
+ * the warning-set of this Preprocessor.
+ */
+ public boolean getWarning(Warning w) {
+ return warnings.contains(w);
+ }
+
+ /**
+ * Adds input for the Preprocessor.
+ *
+ * Inputs are processed in the order in which they are added.
+ */
+ public void addInput(Source source) {
+ source.init(this);
+ inputs.add(source);
+ }
+
+ /**
+ * Adds input for the Preprocessor.
+ *
+ * @see #addInput(Source)
+ */
+ public void addInput(File file)
+ throws IOException {
+ addInput(new FileLexerSource(file));
+ }
+
+ /**
+ * Handles an error.
+ *
+ * If a PreprocessorListener is installed, it receives the
+ * error. Otherwise, an exception is thrown.
+ */
+ protected void error(int line, int column, String msg)
+ throws LexerException {
+ if (listener != null)
+ listener.handleError(source, line, column, msg);
+ else
+ throw new LexerException("Error at " + line + ":" + column + ": " + msg);
+ }
+
+ /**
+ * Handles an error.
+ *
+ * If a PreprocessorListener is installed, it receives the
+ * error. Otherwise, an exception is thrown.
+ *
+ * @see #error(int, int, String)
+ */
+ protected void error(Token tok, String msg)
+ throws LexerException {
+ error(tok.getLine(), tok.getColumn(), msg);
+ }
+
+ /**
+ * Handles a warning.
+ *
+ * If a PreprocessorListener is installed, it receives the
+ * warning. Otherwise, an exception is thrown.
+ */
+ protected void warning(int line, int column, String msg)
+ throws LexerException {
+ if (warnings.contains(Warning.ERROR))
+ error(line, column, msg);
+ else if (listener != null)
+ listener.handleWarning(source, line, column, msg);
+ else
+ throw new LexerException("Warning at " + line + ":" + column + ": " + msg);
+ }
+
+ /**
+ * Handles a warning.
+ *
+ * If a PreprocessorListener is installed, it receives the
+ * warning. Otherwise, an exception is thrown.
+ *
+ * @see #warning(int, int, String)
+ */
+ protected void warning(Token tok, String msg)
+ throws LexerException {
+ warning(tok.getLine(), tok.getColumn(), msg);
+ }
+
+ /**
+ * Adds a Macro to this Preprocessor.
+ *
+ * The given {@link Macro} object encapsulates both the name
+ * and the expansion.
+ */
+ public void addMacro(Macro m) throws LexerException {
+ // System.out.println("Macro " + m);
+ String name = m.getName();
+ /* Already handled as a source error in macro(). */
+ if ("defined".equals(name))
+ throw new LexerException("Cannot redefine name 'defined'");
+ macros.put(m.getName(), m);
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * The String value is lexed into a token stream, which is
+ * used as the macro expansion.
+ */
+ public void addMacro(String name, String value)
+ throws LexerException {
+ try {
+ Macro m = new Macro(name);
+ StringLexerSource s = new StringLexerSource(value);
+ for (;;) {
+ Token tok = s.token();
+ if (tok.getType() == EOF)
+ break;
+ m.addToken(tok);
+ }
+ addMacro(m);
+ } catch (IOException e) {
+ throw new LexerException(e);
+ }
+ }
+
+ /**
+ * Defines the given name as a macro, with the value <code>1</code>.
+ *
+ * This is a convnience method, and is equivalent to
+ * <code>addMacro(name, "1")</code>.
+ */
+ public void addMacro(String name)
+ throws LexerException {
+ addMacro(name, "1");
+ }
+
+ /**
+ * Sets the user include path used by this Preprocessor.
+ */
+ /* Note for future: Create an IncludeHandler? */
+ public void setQuoteIncludePath(List<String> path) {
+ this.quoteincludepath = path;
+ }
+
+ /**
+ * Returns the user include-path of this Preprocessor.
+ *
+ * This list may be freely modified by user code.
+ */
+ public List<String> getQuoteIncludePath() {
+ return quoteincludepath;
+ }
+
+ /**
+ * Sets the system include path used by this Preprocessor.
+ */
+ /* Note for future: Create an IncludeHandler? */
+ public void setSystemIncludePath(List<String> path) {
+ this.sysincludepath = path;
+ }
+
+ /**
+ * Returns the system include-path of this Preprocessor.
+ *
+ * This list may be freely modified by user code.
+ */
+ public List<String> getSystemIncludePath() {
+ return sysincludepath;
+ }
+
+ /**
+ * Sets the Objective-C frameworks path used by this Preprocessor.
+ */
+ /* Note for future: Create an IncludeHandler? */
+ public void setFrameworksPath(List<String> path) {
+ this.frameworkspath = path;
+ }
+
+ /**
+ * Returns the Objective-C frameworks path used by this
+ * Preprocessor.
+ *
+ * This list may be freely modified by user code.
+ */
+ public List<String> getFrameworksPath() {
+ return frameworkspath;
+ }
+
+ /**
+ * Returns the Map of Macros parsed during the run of this
+ * Preprocessor.
+ */
+ public Map<String, Macro> getMacros() {
+ return macros;
+ }
+
+ /**
+ * Returns the named macro.
+ *
+ * While you can modify the returned object, unexpected things
+ * might happen if you do.
+ */
+ public Macro getMacro(String name) {
+ return macros.get(name);
+ }
+
+ /* States */
+ private void push_state() {
+ State top = states.peek();
+ states.push(new State(top));
+ }
+
+ private void pop_state()
+ throws LexerException {
+ State s = states.pop();
+ if (states.isEmpty()) {
+ error(0, 0, "#" + "endif without #" + "if");
+ states.push(s);
+ }
+ }
+
+ private boolean isActive() {
+ State state = states.peek();
+ return state.isParentActive() && state.isActive();
+ }
+
+
+ /* Sources */
+ /**
+ * Returns the top Source on the input stack.
+ *
+ * @see Source
+ * @see #push_source(Source,boolean)
+ * @see #pop_source()
+ */
+ protected Source getSource() {
+ return source;
+ }
+
+ /**
+ * Pushes a Source onto the input stack.
+ *
+ * @see #getSource()
+ * @see #pop_source()
+ */
+ protected void push_source(Source source, boolean autopop) {
+ source.init(this);
+ source.setParent(this.source, autopop);
+ // source.setListener(listener);
+ if (listener != null)
+ listener.handleSourceChange(this.source, "suspend");
+ this.source = source;
+ if (listener != null)
+ listener.handleSourceChange(this.source, "push");
+ }
+
+ /**
+ * Pops a Source from the input stack.
+ *
+ * @see #getSource()
+ * @see #push_source(Source,boolean)
+ */
+ protected void pop_source()
+ throws IOException {
+ if (listener != null)
+ listener.handleSourceChange(this.source, "pop");
+ Source s = this.source;
+ this.source = s.getParent();
+ /* Always a noop unless called externally. */
+ s.close();
+ if (listener != null && this.source != null)
+ listener.handleSourceChange(this.source, "resume");
+ }
+
+
+ /* Source tokens */
+ private Token source_token;
+
+ /* XXX Make this include the NL, and make all cpp directives eat
+ * their own NL. */
+ private Token line_token(int line, String name, String extra) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("#line ").append(line)
+ .append(" \"");
+ /* XXX This call to escape(name) is correct but ugly. */
+ MacroTokenSource.escape(buf, name);
+ buf.append("\"").append(extra).append("\n");
+ return new Token(P_LINE, line, 0, buf.toString(), null);
+ }
+
+ private Token source_token()
+ throws IOException,
+ LexerException {
+ if (source_token != null) {
+ Token tok = source_token;
+ source_token = null;
+ if (getFeature(Feature.DEBUG))
+ System.err.println("Returning unget token " + tok);
+ return tok;
+ }
+
+ for (;;) {
+ Source s = getSource();
+ if (s == null) {
+ if (inputs.isEmpty())
+ return new Token(EOF);
+ Source t = inputs.remove(0);
+ push_source(t, true);
+ if (getFeature(Feature.LINEMARKERS))
+ return line_token(t.getLine(), t.getName(), " 1");
+ continue;
+ }
+ Token tok = s.token();
+ /* XXX Refactor with skipline() */
+ if (tok.getType() == EOF && s.isAutopop()) {
+ // System.out.println("Autopop " + s);
+ pop_source();
+ Source t = getSource();
+ if (getFeature(Feature.LINEMARKERS)
+ && s.isNumbered()
+ && t != null) {
+ /* We actually want 'did the nested source
+ * contain a newline token', which isNumbered()
+ * approximates. This is not perfect, but works. */
+ return line_token(t.getLine() + 1, t.getName(), " 2");
+ }
+ continue;
+ }
+ if (getFeature(Feature.DEBUG))
+ System.err.println("Returning fresh token " + tok);
+ return tok;
+ }
+ }
+
+ private void source_untoken(Token tok) {
+ if (this.source_token != null)
+ throw new IllegalStateException("Cannot return two tokens");
+ this.source_token = tok;
+ }
+
+ private boolean isWhite(Token tok) {
+ int type = tok.getType();
+ return (type == WHITESPACE)
+ || (type == CCOMMENT)
+ || (type == CPPCOMMENT);
+ }
+
+ private Token source_token_nonwhite()
+ throws IOException,
+ LexerException {
+ Token tok;
+ do {
+ tok = source_token();
+ } while (isWhite(tok));
+ return tok;
+ }
+
+ /**
+ * Returns an NL or an EOF token.
+ *
+ * The metadata on the token will be correct, which is better
+ * than generating a new one.
+ *
+ * This method can, as of recent patches, return a P_LINE token.
+ */
+ private Token source_skipline(boolean white)
+ throws IOException,
+ LexerException {
+ // (new Exception("skipping line")).printStackTrace(System.out);
+ Source s = getSource();
+ Token tok = s.skipline(white);
+ /* XXX Refactor with source_token() */
+ if (tok.getType() == EOF && s.isAutopop()) {
+ // System.out.println("Autopop " + s);
+ pop_source();
+ Source t = getSource();
+ if (getFeature(Feature.LINEMARKERS)
+ && s.isNumbered()
+ && t != null) {
+ /* We actually want 'did the nested source
+ * contain a newline token', which isNumbered()
+ * approximates. This is not perfect, but works. */
+ return line_token(t.getLine() + 1, t.getName(), " 2");
+ }
+ }
+ return tok;
+ }
+
+ /* processes and expands a macro. */
+ private boolean macro(Macro m, Token orig)
+ throws IOException,
+ LexerException {
+ Token tok;
+ List<Argument> args;
+
+ // System.out.println("pp: expanding " + m);
+ if (m.isFunctionLike()) {
+ OPEN:
+ for (;;) {
+ tok = source_token();
+ // System.out.println("pp: open: token is " + tok);
+ switch (tok.getType()) {
+ case WHITESPACE: /* XXX Really? */
+
+ case CCOMMENT:
+ case CPPCOMMENT:
+ case NL:
+ break; /* continue */
+
+ case '(':
+ break OPEN;
+ default:
+ source_untoken(tok);
+ return false;
+ }
+ }
+
+ // tok = expanded_token_nonwhite();
+ tok = source_token_nonwhite();
+
+ /* We either have, or we should have args.
+ * This deals elegantly with the case that we have
+ * one empty arg. */
+ if (tok.getType() != ')' || m.getArgs() > 0) {
+ args = new ArrayList<Argument>();
+
+ Argument arg = new Argument();
+ int depth = 0;
+ boolean space = false;
+
+ ARGS:
+ for (;;) {
+ // System.out.println("pp: arg: token is " + tok);
+ switch (tok.getType()) {
+ case EOF:
+ error(tok, "EOF in macro args");
+ return false;
+
+ case ',':
+ if (depth == 0) {
+ if (m.isVariadic()
+ && /* We are building the last arg. */ args.size() == m.getArgs() - 1) {
+ /* Just add the comma. */
+ arg.addToken(tok);
+ } else {
+ args.add(arg);
+ arg = new Argument();
+ }
+ } else {
+ arg.addToken(tok);
+ }
+ space = false;
+ break;
+ case ')':
+ if (depth == 0) {
+ args.add(arg);
+ break ARGS;
+ } else {
+ depth--;
+ arg.addToken(tok);
+ }
+ space = false;
+ break;
+ case '(':
+ depth++;
+ arg.addToken(tok);
+ space = false;
+ break;
+
+ case WHITESPACE:
+ case CCOMMENT:
+ case CPPCOMMENT:
+ /* Avoid duplicating spaces. */
+ space = true;
+ break;
+
+ default:
+ /* Do not put space on the beginning of
+ * an argument token. */
+ if (space && !arg.isEmpty())
+ arg.addToken(Token.space);
+ arg.addToken(tok);
+ space = false;
+ break;
+
+ }
+ // tok = expanded_token();
+ tok = source_token();
+ }
+ /* space may still be true here, thus trailing space
+ * is stripped from arguments. */
+
+ if (args.size() != m.getArgs()) {
+ error(tok,
+ "macro " + m.getName()
+ + " has " + m.getArgs() + " parameters "
+ + "but given " + args.size() + " args");
+ /* We could replay the arg tokens, but I
+ * note that GNU cpp does exactly what we do,
+ * i.e. output the macro name and chew the args.
+ */
+ return false;
+ }
+
+ /*
+ for (Argument a : args)
+ a.expand(this);
+ */
+ for (int i = 0; i < args.size(); i++) {
+ args.get(i).expand(this);
+ }
+
+ // System.out.println("Macro " + m + " args " + args);
+ } else {
+ /* nargs == 0 and we (correctly) got () */
+ args = null;
+ }
+
+ } else {
+ /* Macro without args. */
+ args = null;
+ }
+
+ 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()))}
+ ), true);
+ } else if (m == __FILE__) {
+ StringBuilder buf = new StringBuilder("\"");
+ String name = getSource().getName();
+ if (name == null)
+ name = "<no file>";
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ switch (c) {
+ case '\\':
+ buf.append("\\\\");
+ break;
+ case '"':
+ buf.append("\\\"");
+ break;
+ default:
+ buf.append(c);
+ break;
+ }
+ }
+ buf.append("\"");
+ String text = buf.toString();
+ push_source(new FixedTokenSource(
+ new Token[]{new Token(STRING,
+ orig.getLine(), orig.getColumn(),
+ text, text)}
+ ), true);
+ } else if (m == __COUNTER__) {
+ /* This could equivalently have been done by adding
+ * a special Macro subclass which overrides getTokens(). */
+ int value = this.counter++;
+ push_source(new FixedTokenSource(
+ new Token[]{new Token(NUMBER,
+ orig.getLine(), orig.getColumn(),
+ String.valueOf(value),
+ new NumericValue(10, "" + value))}
+ ), true);
+ } else {
+ push_source(new MacroTokenSource(m, args), true);
+ }
+
+ return true;
+ }
+
+ /**
+ * Expands an argument.
+ */
+ /* I'd rather this were done lazily, but doing so breaks spec. */
+ /* pp */ List<Token> expand(List<Token> arg)
+ throws IOException,
+ LexerException {
+ List<Token> expansion = new ArrayList<Token>();
+ boolean space = false;
+
+ push_source(new FixedTokenSource(arg), false);
+
+ EXPANSION:
+ for (;;) {
+ Token tok = expanded_token();
+ switch (tok.getType()) {
+ case EOF:
+ break EXPANSION;
+
+ case WHITESPACE:
+ case CCOMMENT:
+ case CPPCOMMENT:
+ space = true;
+ break;
+
+ default:
+ if (space && !expansion.isEmpty())
+ expansion.add(Token.space);
+ expansion.add(tok);
+ space = false;
+ break;
+ }
+ }
+
+ pop_source();
+
+ return expansion;
+ }
+
+ /* processes a #define directive */
+ private Token define()
+ throws IOException,
+ LexerException {
+ Token tok = source_token_nonwhite();
+ if (tok.getType() != IDENTIFIER) {
+ error(tok, "Expected identifier");
+ return source_skipline(false);
+ }
+ /* if predefined */
+
+ String name = tok.getText();
+ if ("defined".equals(name)) {
+ error(tok, "Cannot redefine name 'defined'");
+ return source_skipline(false);
+ }
+
+ Macro m = new Macro(getSource(), name);
+ List<String> args;
+
+ tok = source_token();
+ if (tok.getType() == '(') {
+ tok = source_token_nonwhite();
+ if (tok.getType() != ')') {
+ args = new ArrayList<String>();
+ ARGS:
+ for (;;) {
+ switch (tok.getType()) {
+ case IDENTIFIER:
+ args.add(tok.getText());
+ break;
+ case NL:
+ case EOF:
+ error(tok,
+ "Unterminated macro parameter list");
+ return tok;
+ default:
+ error(tok,
+ "error in macro parameters: "
+ + tok.getText());
+ return source_skipline(false);
+ }
+ tok = source_token_nonwhite();
+ switch (tok.getType()) {
+ case ',':
+ break;
+ case ELLIPSIS:
+ tok = source_token_nonwhite();
+ if (tok.getType() != ')')
+ error(tok,
+ "ellipsis must be on last argument");
+ m.setVariadic(true);
+ break ARGS;
+ case ')':
+ break ARGS;
+
+ case NL:
+ case EOF:
+ /* Do not skip line. */
+ error(tok,
+ "Unterminated macro parameters");
+ return tok;
+ default:
+ error(tok,
+ "Bad token in macro parameters: "
+ + tok.getText());
+ return source_skipline(false);
+ }
+ tok = source_token_nonwhite();
+ }
+ } else {
+ assert tok.getType() == ')' : "Expected ')'";
+ args = Collections.emptyList();
+ }
+
+ m.setArgs(args);
+ } else {
+ /* For searching. */
+ args = Collections.emptyList();
+ source_untoken(tok);
+ }
+
+ /* Get an expansion for the macro, using indexOf. */
+ boolean space = false;
+ boolean paste = false;
+ int idx;
+
+ /* Ensure no space at start. */
+ tok = source_token_nonwhite();
+ EXPANSION:
+ for (;;) {
+ switch (tok.getType()) {
+ case EOF:
+ break EXPANSION;
+ case NL:
+ break EXPANSION;
+
+ case CCOMMENT:
+ case CPPCOMMENT:
+ /* XXX This is where we implement GNU's cpp -CC. */
+ // break;
+ case WHITESPACE:
+ if (!paste)
+ space = true;
+ break;
+
+ /* Paste. */
+ case PASTE:
+ space = false;
+ paste = true;
+ m.addPaste(new Token(M_PASTE,
+ tok.getLine(), tok.getColumn(),
+ "#" + "#", null));
+ break;
+
+ /* Stringify. */
+ case '#':
+ if (space)
+ m.addToken(Token.space);
+ space = false;
+ Token la = source_token_nonwhite();
+ if (la.getType() == IDENTIFIER
+ && ((idx = args.indexOf(la.getText())) != -1)) {
+ m.addToken(new Token(M_STRING,
+ la.getLine(), la.getColumn(),
+ "#" + la.getText(),
+ Integer.valueOf(idx)));
+ } else {
+ m.addToken(tok);
+ /* Allow for special processing. */
+ source_untoken(la);
+ }
+ break;
+
+ case IDENTIFIER:
+ if (space)
+ m.addToken(Token.space);
+ space = false;
+ paste = false;
+ idx = args.indexOf(tok.getText());
+ if (idx == -1)
+ m.addToken(tok);
+ else
+ m.addToken(new Token(M_ARG,
+ tok.getLine(), tok.getColumn(),
+ tok.getText(),
+ Integer.valueOf(idx)));
+ break;
+
+ default:
+ if (space)
+ m.addToken(Token.space);
+ space = false;
+ paste = false;
+ m.addToken(tok);
+ break;
+ }
+ tok = source_token();
+ }
+
+ if (getFeature(Feature.DEBUG))
+ System.err.println("Defined macro " + m);
+ addMacro(m);
+
+ return tok; /* NL or EOF. */
+
+ }
+
+ private Token undef()
+ throws IOException,
+ LexerException {
+ Token tok = source_token_nonwhite();
+ 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());
+ if (m != null) {
+ /* XXX error if predefined */
+ macros.remove(m.getName());
+ }
+ }
+ return source_skipline(true);
+ }
+
+ /**
+ * Attempts to include the given file.
+ *
+ * User code may override this method to implement a virtual
+ * file system.
+ */
+ private boolean include(VirtualFile file)
+ throws IOException,
+ LexerException {
+ // System.out.println("Try to include " + ((File)file).getAbsolutePath());
+ if (!file.isFile())
+ return false;
+ if (getFeature(Feature.DEBUG))
+ System.err.println("pp: including " + file);
+ push_source(file.getSource(), true);
+ return true;
+ }
+
+ /**
+ * Includes a file from an include path, by name.
+ */
+ private boolean include(Iterable<String> path, String name)
+ throws IOException,
+ LexerException {
+ for (String dir : path) {
+ VirtualFile file = filesystem.getFile(dir, name);
+ if (include(file))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handles an include directive.
+ */
+ private void include(String parent, int line,
+ String name, boolean quoted)
+ throws IOException,
+ LexerException {
+ VirtualFile pdir = null;
+ if (quoted) {
+ VirtualFile pfile = filesystem.getFile(parent);
+ pdir = pfile.getParentFile();
+ VirtualFile ifile = pdir.getChildFile(name);
+ if (include(ifile))
+ return;
+ if (include(quoteincludepath, name))
+ return;
+ }
+
+ if (include(sysincludepath, name))
+ return;
+
+ StringBuilder buf = new StringBuilder();
+ buf.append("File not found: ").append(name);
+ buf.append(" in");
+ if (quoted) {
+ buf.append(" .").append('(').append(pdir).append(')');
+ for (String dir : quoteincludepath)
+ buf.append(" ").append(dir);
+ }
+ for (String dir : sysincludepath)
+ buf.append(" ").append(dir);
+ error(line, 0, buf.toString());
+ }
+
+ private Token include(boolean next)
+ throws IOException,
+ LexerException {
+ LexerSource lexer = (LexerSource) source;
+ try {
+ lexer.setInclude(true);
+ Token tok = token_nonwhite();
+
+ String name;
+ boolean quoted;
+
+ if (tok.getType() == STRING) {
+ /* XXX Use the original text, not the value.
+ * Backslashes must not be treated as escapes here. */
+ StringBuilder buf = new StringBuilder((String) tok.getValue());
+ HEADER:
+ for (;;) {
+ tok = token_nonwhite();
+ switch (tok.getType()) {
+ case STRING:
+ buf.append((String) tok.getValue());
+ break;
+ case NL:
+ case EOF:
+ break HEADER;
+ default:
+ warning(tok,
+ "Unexpected token on #" + "include line");
+ return source_skipline(false);
+ }
+ }
+ name = buf.toString();
+ quoted = true;
+ } else if (tok.getType() == HEADER) {
+ name = (String) tok.getValue();
+ quoted = false;
+ tok = source_skipline(true);
+ } else {
+ error(tok,
+ "Expected string or header, not " + tok.getText());
+ switch (tok.getType()) {
+ case NL:
+ case EOF:
+ return tok;
+ default:
+ /* Only if not a NL or EOF already. */
+ return source_skipline(false);
+ }
+ }
+
+ /* Do the inclusion. */
+ include(source.getPath(), tok.getLine(), name, quoted);
+
+ /* 'tok' is the 'nl' after the include. We use it after the
+ * #line directive. */
+ if (getFeature(Feature.LINEMARKERS))
+ return line_token(1, source.getName(), " 1");
+ return tok;
+ } finally {
+ lexer.setInclude(false);
+ }
+ }
+
+ protected void pragma(Token name, List<Token> value)
+ throws IOException,
+ LexerException {
+ warning(name, "Unknown #" + "pragma: " + name.getText());
+ }
+
+ private Token pragma()
+ throws IOException,
+ LexerException {
+ Token name;
+
+ NAME:
+ for (;;) {
+ Token tok = token();
+ switch (tok.getType()) {
+ case EOF:
+ /* There ought to be a newline before EOF.
+ * At least, in any skipline context. */
+ /* XXX Are we sure about this? */
+ warning(tok,
+ "End of file in #" + "pragma");
+ return tok;
+ case NL:
+ /* This may contain one or more newlines. */
+ warning(tok,
+ "Empty #" + "pragma");
+ return tok;
+ case CCOMMENT:
+ case CPPCOMMENT:
+ case WHITESPACE:
+ continue NAME;
+ case IDENTIFIER:
+ name = tok;
+ break NAME;
+ default:
+ return source_skipline(false);
+ }
+ }
+
+ Token tok;
+ List<Token> value = new ArrayList<Token>();
+ VALUE:
+ for (;;) {
+ tok = token();
+ switch (tok.getType()) {
+ case EOF:
+ /* There ought to be a newline before EOF.
+ * At least, in any skipline context. */
+ /* XXX Are we sure about this? */
+ warning(tok,
+ "End of file in #" + "pragma");
+ break VALUE;
+ case NL:
+ /* This may contain one or more newlines. */
+ break VALUE;
+ case CCOMMENT:
+ case CPPCOMMENT:
+ break;
+ case WHITESPACE:
+ value.add(tok);
+ break;
+ default:
+ value.add(tok);
+ break;
+ }
+ }
+
+ pragma(name, value);
+
+ return tok; /* The NL. */
+
+ }
+
+ /* For #error and #warning. */
+ private void error(Token pptok, boolean is_error)
+ throws IOException,
+ LexerException {
+ StringBuilder buf = new StringBuilder();
+ buf.append('#').append(pptok.getText()).append(' ');
+ /* Peculiar construction to ditch first whitespace. */
+ Token tok = source_token_nonwhite();
+ ERROR:
+ for (;;) {
+ switch (tok.getType()) {
+ case NL:
+ case EOF:
+ break ERROR;
+ default:
+ buf.append(tok.getText());
+ break;
+ }
+ tok = source_token();
+ }
+ if (is_error)
+ error(pptok, buf.toString());
+ else
+ warning(pptok, buf.toString());
+ }
+
+ /* This bypasses token() for #elif expressions.
+ * If we don't do this, then isActive() == false
+ * causes token() to simply chew the entire input line. */
+ private Token expanded_token()
+ throws IOException,
+ LexerException {
+ for (;;) {
+ Token tok = source_token();
+ // System.out.println("Source token is " + tok);
+ if (tok.getType() == IDENTIFIER) {
+ Macro m = macros.get(tok.getText());
+ if (m == null)
+ return tok;
+ if (source.isExpanding(m))
+ return tok;
+ if (macro(m, tok))
+ continue;
+ }
+ return tok;
+ }
+ }
+
+ private Token expanded_token_nonwhite()
+ throws IOException,
+ LexerException {
+ Token tok;
+ do {
+ tok = expanded_token();
+ // System.out.println("expanded token is " + tok);
+ } while (isWhite(tok));
+ return tok;
+ }
+
+ private Token expr_token = null;
+
+ private Token expr_token()
+ throws IOException,
+ LexerException {
+ Token tok = expr_token;
+
+ if (tok != null) {
+ // System.out.println("ungetting");
+ expr_token = null;
+ } else {
+ tok = expanded_token_nonwhite();
+ // System.out.println("expt is " + tok);
+
+ if (tok.getType() == IDENTIFIER
+ && tok.getText().equals("defined")) {
+ Token la = source_token_nonwhite();
+ boolean paren = false;
+ if (la.getType() == '(') {
+ paren = true;
+ la = source_token_nonwhite();
+ }
+
+ // System.out.println("Core token is " + la);
+ if (la.getType() != IDENTIFIER) {
+ error(la,
+ "defined() needs identifier, not "
+ + la.getText());
+ tok = new Token(NUMBER,
+ la.getLine(), la.getColumn(),
+ "0", new NumericValue(10, "0"));
+ } else if (macros.containsKey(la.getText())) {
+ // System.out.println("Found macro");
+ tok = new Token(NUMBER,
+ la.getLine(), la.getColumn(),
+ "1", new NumericValue(10, "1"));
+ } else {
+ // System.out.println("Not found macro");
+ tok = new Token(NUMBER,
+ la.getLine(), la.getColumn(),
+ "0", new NumericValue(10, "0"));
+ }
+
+ if (paren) {
+ la = source_token_nonwhite();
+ if (la.getType() != ')') {
+ expr_untoken(la);
+ error(la, "Missing ) in defined()");
+ }
+ }
+ }
+ }
+
+ // System.out.println("expr_token returns " + tok);
+ return tok;
+ }
+
+ private void expr_untoken(Token tok)
+ throws LexerException {
+ if (expr_token != null)
+ throw new InternalException(
+ "Cannot unget two expression tokens."
+ );
+ expr_token = tok;
+ }
+
+ private int expr_priority(Token op) {
+ switch (op.getType()) {
+ case '/':
+ return 11;
+ case '%':
+ return 11;
+ case '*':
+ return 11;
+ case '+':
+ return 10;
+ case '-':
+ return 10;
+ case LSH:
+ return 9;
+ case RSH:
+ return 9;
+ case '<':
+ return 8;
+ case '>':
+ return 8;
+ case LE:
+ return 8;
+ case GE:
+ return 8;
+ case EQ:
+ return 7;
+ case NE:
+ return 7;
+ case '&':
+ return 6;
+ case '^':
+ return 5;
+ case '|':
+ return 4;
+ case LAND:
+ return 3;
+ case LOR:
+ return 2;
+ case '?':
+ return 1;
+ default:
+ // System.out.println("Unrecognised operator " + op);
+ return 0;
+ }
+ }
+
+ private long expr(int priority)
+ throws IOException,
+ LexerException {
+ /*
+ System.out.flush();
+ (new Exception("expr(" + priority + ") called")).printStackTrace();
+ System.err.flush();
+ */
+
+ Token tok = expr_token();
+ long lhs, rhs;
+
+ // System.out.println("Expr lhs token is " + tok);
+ switch (tok.getType()) {
+ case '(':
+ lhs = expr(0);
+ tok = expr_token();
+ if (tok.getType() != ')') {
+ expr_untoken(tok);
+ error(tok, "missing ) in expression");
+ return 0;
+ }
+ break;
+
+ case '~':
+ lhs = ~expr(11);
+ break;
+ case '!':
+ lhs = expr(11) == 0 ? 1 : 0;
+ break;
+ case '-':
+ lhs = -expr(11);
+ break;
+ case NUMBER:
+ NumericValue value = (NumericValue) tok.getValue();
+ lhs = value.longValue();
+ break;
+ case CHARACTER:
+ lhs = (long) ((Character) tok.getValue()).charValue();
+ break;
+ case IDENTIFIER:
+ if (warnings.contains(Warning.UNDEF))
+ warning(tok, "Undefined token '" + tok.getText()
+ + "' encountered in conditional.");
+ lhs = 0;
+ break;
+
+ default:
+ expr_untoken(tok);
+ error(tok,
+ "Bad token in expression: " + tok.getText());
+ return 0;
+ }
+
+ EXPR:
+ for (;;) {
+ // System.out.println("expr: lhs is " + lhs + ", pri = " + priority);
+ Token op = expr_token();
+ int pri = expr_priority(op); /* 0 if not a binop. */
+
+ if (pri == 0 || priority >= pri) {
+ expr_untoken(op);
+ break EXPR;
+ }
+ rhs = expr(pri);
+ // System.out.println("rhs token is " + rhs);
+ switch (op.getType()) {
+ case '/':
+ if (rhs == 0) {
+ error(op, "Division by zero");
+ lhs = 0;
+ } else {
+ lhs = lhs / rhs;
+ }
+ break;
+ case '%':
+ if (rhs == 0) {
+ error(op, "Modulus by zero");
+ lhs = 0;
+ } else {
+ lhs = lhs % rhs;
+ }
+ break;
+ case '*':
+ lhs = lhs * rhs;
+ break;
+ case '+':
+ lhs = lhs + rhs;
+ break;
+ case '-':
+ lhs = lhs - rhs;
+ break;
+ case '<':
+ lhs = lhs < rhs ? 1 : 0;
+ break;
+ case '>':
+ lhs = lhs > rhs ? 1 : 0;
+ break;
+ case '&':
+ lhs = lhs & rhs;
+ break;
+ case '^':
+ lhs = lhs ^ rhs;
+ break;
+ case '|':
+ lhs = lhs | rhs;
+ break;
+
+ case LSH:
+ lhs = lhs << rhs;
+ break;
+ case RSH:
+ lhs = lhs >> rhs;
+ break;
+ case LE:
+ lhs = lhs <= rhs ? 1 : 0;
+ break;
+ case GE:
+ lhs = lhs >= rhs ? 1 : 0;
+ break;
+ case EQ:
+ lhs = lhs == rhs ? 1 : 0;
+ break;
+ case NE:
+ lhs = lhs != rhs ? 1 : 0;
+ break;
+ case LAND:
+ lhs = (lhs != 0) && (rhs != 0) ? 1 : 0;
+ break;
+ case LOR:
+ lhs = (lhs != 0) || (rhs != 0) ? 1 : 0;
+ break;
+
+ case '?':
+ /* XXX Handle this? */
+
+ default:
+ error(op,
+ "Unexpected operator " + op.getText());
+ return 0;
+
+ }
+ }
+
+ /*
+ System.out.flush();
+ (new Exception("expr returning " + lhs)).printStackTrace();
+ System.err.flush();
+ */
+ // System.out.println("expr returning " + lhs);
+ return lhs;
+ }
+
+ private Token toWhitespace(Token tok) {
+ String text = tok.getText();
+ int len = text.length();
+ boolean cr = false;
+ int nls = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = text.charAt(i);
+
+ switch (c) {
+ case '\r':
+ cr = true;
+ nls++;
+ break;
+ case '\n':
+ if (cr) {
+ cr = false;
+ break;
+ }
+ /* fallthrough */
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ cr = false;
+ nls++;
+ break;
+ }
+ }
+
+ char[] cbuf = new char[nls];
+ Arrays.fill(cbuf, '\n');
+ return new Token(WHITESPACE,
+ tok.getLine(), tok.getColumn(),
+ new String(cbuf));
+ }
+
+ @Nonnull
+ private Token _token()
+ throws IOException,
+ LexerException {
+
+ for (;;) {
+ Token tok;
+ if (!isActive()) {
+ try {
+ /* XXX Tell lexer to ignore warnings. */
+ source.setActive(false);
+ tok = source_token();
+ } finally {
+ /* XXX Tell lexer to stop ignoring warnings. */
+ source.setActive(true);
+ }
+ switch (tok.getType()) {
+ case HASH:
+ case NL:
+ case EOF:
+ /* The preprocessor has to take action here. */
+ break;
+ case WHITESPACE:
+ return tok;
+ case CCOMMENT:
+ case CPPCOMMENT:
+ // Patch up to preserve whitespace.
+ if (getFeature(Feature.KEEPALLCOMMENTS))
+ return tok;
+ if (!isActive())
+ return toWhitespace(tok);
+ if (getFeature(Feature.KEEPCOMMENTS))
+ return tok;
+ return toWhitespace(tok);
+ default:
+ // Return NL to preserve whitespace.
+ /* XXX This might lose a comment. */
+ return source_skipline(false);
+ }
+ } else {
+ tok = source_token();
+ }
+
+ LEX:
+ switch (tok.getType()) {
+ case EOF:
+ /* Pop the stacks. */
+ return tok;
+
+ case WHITESPACE:
+ case NL:
+ return tok;
+
+ case CCOMMENT:
+ case CPPCOMMENT:
+ return tok;
+
+ case '!':
+ case '%':
+ case '&':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '-':
+ case '/':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '[':
+ case ']':
+ case '^':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ case '.':
+
+ /* From Olivier Chafik for Objective C? */
+ case '@':
+ /* The one remaining ASCII, might as well. */
+ case '`':
+
+ // case '#':
+ case AND_EQ:
+ case ARROW:
+ case CHARACTER:
+ case DEC:
+ case DIV_EQ:
+ case ELLIPSIS:
+ case EQ:
+ case GE:
+ case HEADER: /* Should only arise from include() */
+
+ case INC:
+ case LAND:
+ case LE:
+ case LOR:
+ case LSH:
+ case LSH_EQ:
+ case SUB_EQ:
+ case MOD_EQ:
+ case MULT_EQ:
+ case NE:
+ case OR_EQ:
+ case PLUS_EQ:
+ case RANGE:
+ case RSH:
+ case RSH_EQ:
+ case STRING:
+ case XOR_EQ:
+ return tok;
+
+ case NUMBER:
+ return tok;
+
+ case IDENTIFIER:
+ Macro m = macros.get(tok.getText());
+ if (m == null)
+ return tok;
+ if (source.isExpanding(m))
+ return tok;
+ if (macro(m, tok))
+ break;
+ return tok;
+
+ case P_LINE:
+ if (getFeature(Feature.LINEMARKERS))
+ return tok;
+ break;
+
+ case INVALID:
+ if (getFeature(Feature.CSYNTAX))
+ error(tok, String.valueOf(tok.getValue()));
+ return tok;
+
+ default:
+ throw new InternalException("Bad token " + tok);
+ // break;
+
+ case HASH:
+ tok = source_token_nonwhite();
+ // (new Exception("here")).printStackTrace();
+ switch (tok.getType()) {
+ case NL:
+ break LEX; /* Some code has #\n */
+
+ case IDENTIFIER:
+ break;
+ default:
+ error(tok,
+ "Preprocessor directive not a word "
+ + tok.getText());
+ return source_skipline(false);
+ }
+ PreprocessorCommand ppcmd = PreprocessorCommand.forText(tok.getText());
+ if (ppcmd == null) {
+ error(tok,
+ "Unknown preprocessor directive "
+ + tok.getText());
+ return source_skipline(false);
+ }
+
+ PP:
+ switch (ppcmd) {
+
+ case PP_DEFINE:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ return define();
+ // break;
+
+ case PP_UNDEF:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ return undef();
+ // break;
+
+ case PP_INCLUDE:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ return include(false);
+ // break;
+ case PP_INCLUDE_NEXT:
+ if (!isActive())
+ return source_skipline(false);
+ if (!getFeature(Feature.INCLUDENEXT)) {
+ error(tok,
+ "Directive include_next not enabled"
+ );
+ return source_skipline(false);
+ }
+ return include(true);
+ // break;
+
+ case PP_WARNING:
+ case PP_ERROR:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ error(tok, ppcmd == PP_ERROR);
+ break;
+
+ case PP_IF:
+ push_state();
+ if (!isActive()) {
+ return source_skipline(false);
+ }
+ expr_token = null;
+ states.peek().setActive(expr(0) != 0);
+ tok = expr_token(); /* unget */
+
+ if (tok.getType() == NL)
+ return tok;
+ return source_skipline(true);
+ // break;
+
+ case PP_ELIF:
+ State state = states.peek();
+ if (false) {
+ /* Check for 'if' */;
+ } else if (state.sawElse()) {
+ error(tok,
+ "#elif after #" + "else");
+ return source_skipline(false);
+ } else if (!state.isParentActive()) {
+ /* Nested in skipped 'if' */
+ return source_skipline(false);
+ } else if (state.isActive()) {
+ /* The 'if' part got executed. */
+ state.setParentActive(false);
+ /* This is like # else # if but with
+ * only one # end. */
+ state.setActive(false);
+ return source_skipline(false);
+ } else {
+ expr_token = null;
+ state.setActive(expr(0) != 0);
+ tok = expr_token(); /* unget */
+
+ if (tok.getType() == NL)
+ return tok;
+ return source_skipline(true);
+ }
+ // break;
+
+ case PP_ELSE:
+ state = states.peek();
+ 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());
+ return source_skipline(warnings.contains(Warning.ENDIF_LABELS));
+ }
+ // break;
+
+ case PP_IFDEF:
+ push_state();
+ if (!isActive()) {
+ return source_skipline(false);
+ } else {
+ tok = source_token_nonwhite();
+ // System.out.println("ifdef " + tok);
+ if (tok.getType() != IDENTIFIER) {
+ error(tok,
+ "Expected identifier, not "
+ + tok.getText());
+ return source_skipline(false);
+ } else {
+ String text = tok.getText();
+ boolean exists
+ = macros.containsKey(text);
+ states.peek().setActive(exists);
+ return source_skipline(true);
+ }
+ }
+ // break;
+
+ case PP_IFNDEF:
+ push_state();
+ if (!isActive()) {
+ return source_skipline(false);
+ } else {
+ tok = source_token_nonwhite();
+ if (tok.getType() != IDENTIFIER) {
+ error(tok,
+ "Expected identifier, not "
+ + tok.getText());
+ return source_skipline(false);
+ } else {
+ String text = tok.getText();
+ boolean exists
+ = macros.containsKey(text);
+ states.peek().setActive(!exists);
+ return source_skipline(true);
+ }
+ }
+ // break;
+
+ case PP_ENDIF:
+ pop_state();
+ return source_skipline(warnings.contains(Warning.ENDIF_LABELS));
+ // break;
+
+ case PP_LINE:
+ return source_skipline(false);
+ // break;
+
+ case PP_PRAGMA:
+ if (!isActive())
+ return source_skipline(false);
+ return pragma();
+ // break;
+
+ default:
+ /* Actual unknown directives are
+ * processed above. If we get here,
+ * we succeeded the map lookup but
+ * failed to handle it. Therefore,
+ * this is (unconditionally?) fatal. */
+ // if (isActive()) /* XXX Could be warning. */
+ throw new InternalException(
+ "Internal error: Unknown directive "
+ + tok);
+ // return source_skipline(false);
+ }
+
+ }
+ }
+ }
+
+ private Token token_nonwhite()
+ throws IOException,
+ LexerException {
+ Token tok;
+ do {
+ tok = _token();
+ } while (isWhite(tok));
+ return tok;
+ }
+
+ /**
+ * Returns the next preprocessor token.
+ *
+ * @see Token
+ * @throws LexerException if a preprocessing error occurs.
+ * @throws InternalException if an unexpected error condition arises.
+ */
+ @Nonnull
+ public Token token()
+ throws IOException,
+ LexerException {
+ Token tok = _token();
+ if (getFeature(Feature.DEBUG))
+ System.err.println("pp: Returning " + tok);
+ return tok;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+
+ Source s = getSource();
+ while (s != null) {
+ buf.append(" -> ").append(String.valueOf(s)).append("\n");
+ s = s.getParent();
+ }
+
+ Map<String, Macro> macros = getMacros();
+ List<String> keys = new ArrayList<String>(
+ macros.keySet()
+ );
+ Collections.sort(keys);
+ Iterator<String> mt = keys.iterator();
+ while (mt.hasNext()) {
+ String key = mt.next();
+ Macro macro = macros.get(key);
+ buf.append("#").append("macro ").append(macro).append("\n");
+ }
+
+ return buf.toString();
+ }
+
+ @Override
+ public void close()
+ throws IOException {
+ {
+ Source s = source;
+ while (s != null) {
+ s.close();
+ s = s.getParent();
+ }
+ }
+ for (Source s : inputs) {
+ s.close();
+ }
+ }
+
+}
diff --git a/src/main/java/org/anarres/cpp/PreprocessorCommand.java b/src/main/java/org/anarres/cpp/PreprocessorCommand.java
new file mode 100644
index 0000000..3938360
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/PreprocessorCommand.java
@@ -0,0 +1,44 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anarres.cpp;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ *
+ * @author shevek
+ */
+public enum PreprocessorCommand {
+
+ PP_DEFINE("define"),
+ PP_ELIF("elif"),
+ PP_ELSE("else"),
+ PP_ENDIF("endif"),
+ PP_ERROR("error"),
+ PP_IF("if"),
+ PP_IFDEF("ifdef"),
+ PP_IFNDEF("ifndef"),
+ PP_INCLUDE("include"),
+ PP_LINE("line"),
+ PP_PRAGMA("pragma"),
+ PP_UNDEF("undef"),
+ PP_WARNING("warning"),
+ PP_INCLUDE_NEXT("include_next"),
+ PP_IMPORT("import");
+ private final String text;
+ /* pp */ PreprocessorCommand(String text) {
+ this.text = text;
+ }
+
+ @CheckForNull
+ public static PreprocessorCommand forText(@Nonnull String text) {
+ for (PreprocessorCommand ppcmd : PreprocessorCommand.values())
+ if (ppcmd.text.equals(text))
+ return ppcmd;
+ return null;
+ }
+}
diff --git a/src/main/java/org/anarres/cpp/PreprocessorListener.java b/src/main/java/org/anarres/cpp/PreprocessorListener.java
new file mode 100644
index 0000000..a5b4339
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/PreprocessorListener.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+/**
+ * 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 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);
+ }
+
+ /**
+ * 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) {
+ }
+
+}
diff --git a/src/main/java/org/anarres/cpp/ResourceFileSystem.java b/src/main/java/org/anarres/cpp/ResourceFileSystem.java
new file mode 100644
index 0000000..7efd664
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/ResourceFileSystem.java
@@ -0,0 +1,81 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anarres.cpp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import javax.annotation.Nonnull;
+
+/**
+ *
+ * @author shevek
+ */
+public class ResourceFileSystem implements VirtualFileSystem {
+
+ private final ClassLoader loader;
+
+ public ResourceFileSystem(@Nonnull ClassLoader loader) {
+ this.loader = loader;
+ }
+
+ @Override
+ public VirtualFile getFile(String path) {
+ return new ResourceFile(loader, path);
+ }
+
+ @Override
+ public VirtualFile getFile(String dir, String name) {
+ return getFile(dir + "/" + name);
+ }
+
+ private class ResourceFile implements VirtualFile {
+
+ private final ClassLoader loader;
+ private final String path;
+
+ public ResourceFile(ClassLoader loader, String path) {
+ this.loader = loader;
+ this.path = path;
+ }
+
+ @Override
+ public boolean isFile() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public String getName() {
+ return path.substring(path.lastIndexOf('/') + 1);
+ }
+
+ @Override
+ public ResourceFile getParentFile() {
+ int idx = path.lastIndexOf('/');
+ if (idx < 1)
+ return null;
+ return new ResourceFile(loader, path.substring(0, idx));
+ }
+
+ @Override
+ public ResourceFile getChildFile(String name) {
+ return new ResourceFile(loader, path + "/" + name);
+ }
+
+ @Override
+ public Source getSource() throws IOException {
+ InputStream stream = loader.getResourceAsStream(path);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ return new LexerSource(reader, true);
+ }
+ }
+}
diff --git a/src/main/java/org/anarres/cpp/Source.java b/src/main/java/org/anarres/cpp/Source.java
new file mode 100644
index 0000000..532f5fc
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/Source.java
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+import static org.anarres.cpp.Token.*;
+
+/**
+ * An input to the Preprocessor.
+ *
+ * Inputs may come from Files, Strings or other sources. The
+ * preprocessor maintains a stack of Sources. Operations such as
+ * file inclusion or token pasting will push a new source onto
+ * the Preprocessor stack. Sources pop from the stack when they
+ * are exhausted; this may be transparent or explicit.
+ *
+ * BUG: Error messages are not handled properly.
+ */
+public abstract class Source implements Iterable<Token>, Closeable {
+
+ private Source parent;
+ private boolean autopop;
+ private PreprocessorListener listener;
+ private boolean active;
+ private boolean werror;
+
+ /* LineNumberReader */
+
+ /*
+ // We can't do this, since we would lose the LexerException
+ private class Itr implements Iterator {
+ private Token next = null;
+ private void advance() {
+ try {
+ if (next != null)
+ next = token();
+ }
+ catch (IOException e) {
+ throw new UnsupportedOperationException(
+ "Failed to advance token iterator: " +
+ e.getMessage()
+ );
+ }
+ }
+ public boolean hasNext() {
+ return next.getType() != EOF;
+ }
+ public Token next() {
+ advance();
+ Token t = next;
+ next = null;
+ return t;
+ }
+ public void remove() {
+ throw new UnsupportedOperationException(
+ "Cannot remove tokens from a Source."
+ );
+ }
+ }
+ */
+ public Source() {
+ this.parent = null;
+ this.autopop = false;
+ this.listener = null;
+ this.active = true;
+ this.werror = false;
+ }
+
+ /**
+ * Sets the parent source of this source.
+ *
+ * Sources form a singly linked list.
+ */
+ /* pp */ void setParent(Source parent, boolean autopop) {
+ this.parent = parent;
+ this.autopop = autopop;
+ }
+
+ /**
+ * Returns the parent source of this source.
+ *
+ * Sources form a singly linked list.
+ */
+ /* pp */ final Source getParent() {
+ return parent;
+ }
+
+
+ // @OverrideMustInvoke
+ /* pp */ void init(Preprocessor pp) {
+ setListener(pp.getListener());
+ this.werror = pp.getWarnings().contains(Warning.ERROR);
+ }
+
+ /**
+ * Sets the listener for this Source.
+ *
+ * Normally this is set by the Preprocessor when a Source is
+ * used, but if you are using a Source as a standalone object,
+ * you may wish to call this.
+ */
+ public void setListener(PreprocessorListener pl) {
+ this.listener = pl;
+ }
+
+ /**
+ * Returns the File currently being lexed.
+ *
+ * If this Source is not a {@link FileLexerSource}, then
+ * it will ask the parent Source, and so forth recursively.
+ * If no Source on the stack is a FileLexerSource, returns null.
+ */
+ /* pp */ String getPath() {
+ Source parent = getParent();
+ if (parent != null)
+ return parent.getPath();
+ return null;
+ }
+
+ /**
+ * Returns the human-readable name of the current Source.
+ */
+ /* pp */ String getName() {
+ Source parent = getParent();
+ if (parent != null)
+ return parent.getName();
+ return null;
+ }
+
+ /**
+ * Returns the current line number within this Source.
+ */
+ public int getLine() {
+ Source parent = getParent();
+ if (parent == null)
+ return 0;
+ return parent.getLine();
+ }
+
+ /**
+ * Returns the current column number within this Source.
+ */
+ public int getColumn() {
+ Source parent = getParent();
+ if (parent == null)
+ return 0;
+ return parent.getColumn();
+ }
+
+ /**
+ * Returns true if this Source is expanding the given macro.
+ *
+ * This is used to prevent macro recursion.
+ */
+ /* pp */ boolean isExpanding(Macro m) {
+ Source parent = getParent();
+ if (parent != null)
+ return parent.isExpanding(m);
+ return false;
+ }
+
+ /**
+ * Returns true if this Source should be transparently popped
+ * from the input stack.
+ *
+ * Examples of such sources are macro expansions.
+ */
+ /* pp */ boolean isAutopop() {
+ return autopop;
+ }
+
+ /**
+ * Returns true if this source has line numbers.
+ */
+ /* pp */ boolean isNumbered() {
+ return false;
+ }
+
+ /* This is an incredibly lazy way of disabling warnings when
+ * the source is not active. */
+ /* pp */ void setActive(boolean b) {
+ this.active = b;
+ }
+
+ /* pp */ boolean isActive() {
+ return active;
+ }
+
+ /**
+ * Returns the next Token parsed from this input stream.
+ *
+ * @see Token
+ */
+ @Nonnull
+ public abstract Token token()
+ throws IOException,
+ LexerException;
+
+ /**
+ * Returns a token iterator for this Source.
+ */
+ @Override
+ public Iterator<Token> iterator() {
+ return new SourceIterator(this);
+ }
+
+ /**
+ * Skips tokens until the end of line.
+ *
+ * @param white true if only whitespace is permitted on the
+ * remainder of the line.
+ * @return the NL token.
+ */
+ @Nonnull
+ public Token skipline(boolean white)
+ throws IOException,
+ LexerException {
+ for (;;) {
+ Token tok = token();
+ switch (tok.getType()) {
+ case EOF:
+ /* There ought to be a newline before EOF.
+ * At least, in any skipline context. */
+ /* XXX Are we sure about this? */
+ warning(tok.getLine(), tok.getColumn(),
+ "No newline before end of file");
+ return new Token(NL,
+ tok.getLine(), tok.getColumn(),
+ "\n");
+ // return tok;
+ case NL:
+ /* This may contain one or more newlines. */
+ return tok;
+ case CCOMMENT:
+ case CPPCOMMENT:
+ case WHITESPACE:
+ break;
+ default:
+ /* XXX Check white, if required. */
+ if (white)
+ warning(tok.getLine(), tok.getColumn(),
+ "Unexpected nonwhite token");
+ break;
+ }
+ }
+ }
+
+ protected void error(int line, int column, String msg)
+ throws LexerException {
+ if (listener != null)
+ listener.handleError(this, line, column, msg);
+ else
+ throw new LexerException("Error at " + line + ":" + column + ": " + msg);
+ }
+
+ protected void warning(int line, int column, String msg)
+ throws LexerException {
+ if (werror)
+ error(line, column, msg);
+ else if (listener != null)
+ listener.handleWarning(this, line, column, msg);
+ else
+ throw new LexerException("Warning at " + line + ":" + column + ": " + msg);
+ }
+
+ public void close()
+ throws IOException {
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/SourceIterator.java b/src/main/java/org/anarres/cpp/SourceIterator.java
index d5c63c7..d5c63c7 100644
--- a/src/java/org/anarres/cpp/SourceIterator.java
+++ b/src/main/java/org/anarres/cpp/SourceIterator.java
diff --git a/src/java/org/anarres/cpp/State.java b/src/main/java/org/anarres/cpp/State.java
index ed5c736..ed5c736 100644
--- a/src/java/org/anarres/cpp/State.java
+++ b/src/main/java/org/anarres/cpp/State.java
diff --git a/src/java/org/anarres/cpp/StringLexerSource.java b/src/main/java/org/anarres/cpp/StringLexerSource.java
index 2285097..8640bc8 100644
--- a/src/java/org/anarres/cpp/StringLexerSource.java
+++ b/src/main/java/org/anarres/cpp/StringLexerSource.java
@@ -14,18 +14,10 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
package org.anarres.cpp;
-import java.io.IOException;
-import java.io.Reader;
import java.io.StringReader;
-import java.util.List;
-import java.util.Iterator;
-
-import static org.anarres.cpp.Token.*;
-
/**
* A Source for lexing a String.
*
@@ -34,27 +26,28 @@ import static org.anarres.cpp.Token.*;
*/
public class StringLexerSource extends LexerSource {
- /**
- * Creates a new Source for lexing the given String.
- *
- * @param ppvalid true if preprocessor directives are to be
- * honoured within the string.
- */
- public StringLexerSource(String string, boolean ppvalid) {
- super(new StringReader(string), ppvalid);
- }
-
- /**
- * Creates a new Source for lexing the given String.
- *
- * By default, preprocessor directives are not honoured within
- * the string.
- */
- public StringLexerSource(String string) {
- this(string, false);
- }
-
- public String toString() {
- return "string literal";
- }
+ /**
+ * Creates a new Source for lexing the given String.
+ *
+ * @param ppvalid true if preprocessor directives are to be
+ * honoured within the string.
+ */
+ public StringLexerSource(String string, boolean ppvalid) {
+ super(new StringReader(string), ppvalid);
+ }
+
+ /**
+ * Creates a new Source for lexing the given String.
+ *
+ * By default, preprocessor directives are not honoured within
+ * the string.
+ */
+ public StringLexerSource(String string) {
+ this(string, false);
+ }
+
+ @Override
+ public String toString() {
+ return "string literal";
+ }
}
diff --git a/src/main/java/org/anarres/cpp/Token.java b/src/main/java/org/anarres/cpp/Token.java
new file mode 100644
index 0000000..3e6eb3e
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/Token.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+package org.anarres.cpp;
+
+/**
+ * A Preprocessor token.
+ *
+ * @see Preprocessor
+ */
+public final class Token {
+
+ // public static final int EOF = -1;
+ private final int type;
+ private int line;
+ private int column;
+ private final Object value;
+ private final String text;
+
+ public Token(int type, int line, int column,
+ String text, Object value) {
+ this.type = type;
+ this.line = line;
+ this.column = column;
+ this.text = text;
+ this.value = value;
+ }
+
+ public Token(int type, int line, int column, String text) {
+ this(type, line, column, text, null);
+ }
+
+ /* pp */ Token(int type, String text, Object value) {
+ this(type, -1, -1, text, value);
+ }
+
+ /* pp */ Token(int type, String text) {
+ this(type, text, null);
+ }
+
+ /* pp */ Token(int type) {
+ this(type, TokenType.getTokenText(type));
+ }
+
+ /**
+ * Returns the semantic type of this token.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /* pp */ void setLocation(int line, int column) {
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * Returns the line at which this token started.
+ *
+ * Lines are numbered from zero.
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * Returns the column at which this token started.
+ *
+ * Columns are numbered from zero.
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * Returns the original or generated text of this token.
+ *
+ * This is distinct from the semantic value of the token.
+ *
+ * @see #getValue()
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Returns the semantic value of this token.
+ *
+ * For strings, this is the parsed String.
+ * For integers, this is an Integer object.
+ * For other token types, as appropriate.
+ *
+ * @see #getText()
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Returns a description of this token, for debugging purposes.
+ */
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+
+ buf.append('[').append(getTokenName(type));
+ if (line != -1) {
+ buf.append('@').append(line);
+ if (column != -1)
+ buf.append(',').append(column);
+ }
+ buf.append("]:");
+ if (text != null)
+ buf.append('"').append(text).append('"');
+ else if (type > 3 && type < 256)
+ buf.append((char) type);
+ else
+ buf.append('<').append(type).append('>');
+ if (value != null)
+ buf.append('=').append(value);
+ return buf.toString();
+ }
+
+ /**
+ * Returns the descriptive name of the given token type.
+ *
+ * This is mostly used for stringification and debugging.
+ */
+ public static String getTokenName(int type) {
+ return TokenType.getTokenName(type);
+ }
+
+ public static final int AND_EQ = 257;
+ public static final int ARROW = 258;
+ public static final int CHARACTER = 259;
+ public static final int CCOMMENT = 260;
+ public static final int CPPCOMMENT = 261;
+ public static final int DEC = 262;
+ public static final int DIV_EQ = 263;
+ public static final int ELLIPSIS = 264;
+ public static final int EOF = 265;
+ public static final int EQ = 266;
+ public static final int GE = 267;
+ public static final int HASH = 268;
+ public static final int HEADER = 269;
+ public static final int IDENTIFIER = 270;
+ public static final int INC = 271;
+ public static final int NUMBER = 272;
+ public static final int LAND = 273;
+ public static final int LAND_EQ = 274;
+ public static final int LE = 275;
+ public static final int LITERAL = 276;
+ public static final int LOR = 277;
+ public static final int LOR_EQ = 278;
+ public static final int LSH = 279;
+ public static final int LSH_EQ = 280;
+ public static final int MOD_EQ = 281;
+ public static final int MULT_EQ = 282;
+ public static final int NE = 283;
+ public static final int NL = 284;
+ public static final int OR_EQ = 285;
+ public static final int PASTE = 286;
+ public static final int PLUS_EQ = 287;
+ public static final int RANGE = 288;
+ public static final int RSH = 289;
+ public static final int RSH_EQ = 290;
+ public static final int SQSTRING = 291;
+ public static final int STRING = 292;
+ public static final int SUB_EQ = 293;
+ public static final int WHITESPACE = 294;
+ public static final int XOR_EQ = 295;
+ public static final int M_ARG = 296;
+ public static final int M_PASTE = 297;
+ public static final int M_STRING = 298;
+ public static final int P_LINE = 299;
+ public static final int INVALID = 300;
+
+ /** The position-less space token. */
+ /* pp */ static final Token space = new Token(WHITESPACE, -1, -1, " ");
+}
diff --git a/src/java/org/anarres/cpp/TokenSnifferSource.java b/src/main/java/org/anarres/cpp/TokenSnifferSource.java
index 1512b2e..1512b2e 100644
--- a/src/java/org/anarres/cpp/TokenSnifferSource.java
+++ b/src/main/java/org/anarres/cpp/TokenSnifferSource.java
diff --git a/src/main/java/org/anarres/cpp/TokenType.java b/src/main/java/org/anarres/cpp/TokenType.java
new file mode 100644
index 0000000..86df097
--- /dev/null
+++ b/src/main/java/org/anarres/cpp/TokenType.java
@@ -0,0 +1,128 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anarres.cpp;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import static org.anarres.cpp.Token.*;
+
+/**
+ *
+ * @author shevek
+ */
+/* pp */ class TokenType {
+
+ private static final List<TokenType> TYPES = new ArrayList<TokenType>();
+
+ private static void addTokenType(@Nonnegative int type, @Nonnull String name, @CheckForNull String text) {
+ while (TYPES.size() <= type)
+ TYPES.add(null);
+ TYPES.set(type, new TokenType(name, text));
+ }
+
+ private static void addTokenType(@Nonnegative int type, @Nonnull String name) {
+ addTokenType(type, name, null);
+ }
+
+ @CheckForNull
+ public static TokenType getTokenType(@Nonnegative int type) {
+ try {
+ return TYPES.get(type);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ @Nonnull
+ public static String getTokenName(@Nonnegative int type) {
+ if (type < 0)
+ return "Invalid" + type;
+ TokenType tokenType = getTokenType(type);
+ if (tokenType == null)
+ return "Unknown" + type;
+ return tokenType.getName();
+ }
+
+ @CheckForNull
+ public static String getTokenText(@Nonnegative int type) {
+ TokenType tokenType = getTokenType(type);
+ if (tokenType == null)
+ return null;
+ return tokenType.getText();
+ }
+
+ static {
+ for (int i = 0; i < 255; i++) {
+ String text = String.valueOf((char) i);
+ addTokenType(i, text, text);
+ }
+ addTokenType(AND_EQ, "AND_EQ", "&=");
+ addTokenType(ARROW, "ARROW", "->");
+ addTokenType(CHARACTER, "CHARACTER");
+ addTokenType(CCOMMENT, "CCOMMENT");
+ addTokenType(CPPCOMMENT, "CPPCOMMENT");
+ addTokenType(DEC, "DEC", "--");
+ addTokenType(DIV_EQ, "DIV_EQ", "/=");
+ addTokenType(ELLIPSIS, "ELLIPSIS", "...");
+ addTokenType(EOF, "EOF");
+ addTokenType(EQ, "EQ", "==");
+ addTokenType(GE, "GE", ">=");
+ addTokenType(HASH, "HASH", "#");
+ addTokenType(HEADER, "HEADER");
+ addTokenType(IDENTIFIER, "IDENTIFIER");
+ addTokenType(INC, "INC", "++");
+ addTokenType(NUMBER, "NUMBER");
+ addTokenType(LAND, "LAND", "&&");
+ addTokenType(LAND_EQ, "LAND_EQ", "&&=");
+ addTokenType(LE, "LE", "<=");
+ addTokenType(LITERAL, "LITERAL");
+ addTokenType(LOR, "LOR", "||");
+ addTokenType(LOR_EQ, "LOR_EQ", "||=");
+ addTokenType(LSH, "LSH", "<<");
+ addTokenType(LSH_EQ, "LSH_EQ", "<<=");
+ addTokenType(MOD_EQ, "MOD_EQ", "%=");
+ addTokenType(MULT_EQ, "MULT_EQ", "*=");
+ addTokenType(NE, "NE", "!=");
+ addTokenType(NL, "NL");
+ addTokenType(OR_EQ, "OR_EQ", "|=");
+ addTokenType(PASTE, "PASTE", "##");
+ addTokenType(PLUS_EQ, "PLUS_EQ", "+=");
+ addTokenType(RANGE, "RANGE", "..");
+ addTokenType(RSH, "RSH", ">>");
+ addTokenType(RSH_EQ, "RSH_EQ", ">>=");
+ addTokenType(SQSTRING, "SQSTRING");
+ addTokenType(STRING, "STRING");
+ addTokenType(SUB_EQ, "SUB_EQ", "-=");
+ addTokenType(WHITESPACE, "WHITESPACE");
+ addTokenType(XOR_EQ, "XOR_EQ", "^=");
+ addTokenType(M_ARG, "M_ARG");
+ addTokenType(M_PASTE, "M_PASTE");
+ addTokenType(M_STRING, "M_STRING");
+ addTokenType(P_LINE, "P_LINE");
+ addTokenType(INVALID, "INVALID");
+ }
+
+ private final String name;
+ private final String text;
+
+ /* pp */ TokenType(@Nonnull String name, @CheckForNull String text) {
+ this.name = name;
+ this.text = text;
+ }
+
+ @Nonnull
+ public String getName() {
+ return name;
+ }
+
+ @CheckForNull
+ public String getText() {
+ return text;
+ }
+}
diff --git a/src/java/org/anarres/cpp/VirtualFile.java b/src/main/java/org/anarres/cpp/VirtualFile.java
index 405891a..aee1cad 100644
--- a/src/java/org/anarres/cpp/VirtualFile.java
+++ b/src/main/java/org/anarres/cpp/VirtualFile.java
@@ -14,20 +14,32 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
package org.anarres.cpp;
import java.io.IOException;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
/**
* An extremely lightweight virtual file interface.
*/
public interface VirtualFile {
- // public String getParent();
- public boolean isFile();
- public String getPath();
- public String getName();
- public VirtualFile getParentFile();
- public VirtualFile getChildFile(String name);
- public Source getSource() throws IOException;
+
+ // public String getParent();
+ public boolean isFile();
+
+ @Nonnull
+ public String getPath();
+
+ @Nonnull
+ public String getName();
+
+ @CheckForNull
+ public VirtualFile getParentFile();
+
+ @Nonnull
+ public VirtualFile getChildFile(String name);
+
+ @Nonnull
+ public Source getSource() throws IOException;
}
diff --git a/src/java/org/anarres/cpp/VirtualFileSystem.java b/src/main/java/org/anarres/cpp/VirtualFileSystem.java
index eb5c4a1..56e53ac 100644
--- a/src/java/org/anarres/cpp/VirtualFileSystem.java
+++ b/src/main/java/org/anarres/cpp/VirtualFileSystem.java
@@ -14,13 +14,14 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
package org.anarres.cpp;
/**
* An extremely lightweight virtual file system interface.
*/
public interface VirtualFileSystem {
- public VirtualFile getFile(String path);
- public VirtualFile getFile(String dir, String name);
+
+ public VirtualFile getFile(String path);
+
+ public VirtualFile getFile(String dir, String name);
}
diff --git a/src/java/org/anarres/cpp/Warning.java b/src/main/java/org/anarres/cpp/Warning.java
index abe38d0..da2b2c6 100644
--- a/src/java/org/anarres/cpp/Warning.java
+++ b/src/main/java/org/anarres/cpp/Warning.java
@@ -14,19 +14,19 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
package org.anarres.cpp;
/**
* Warning classes which may optionally be emitted by the Preprocessor.
*/
public enum Warning {
- TRIGRAPHS,
- // TRADITIONAL,
- IMPORT,
- UNDEF,
- UNUSED_MACROS,
- ENDIF_LABELS,
- ERROR,
+
+ TRIGRAPHS,
+ // TRADITIONAL,
+ IMPORT,
+ UNDEF,
+ UNUSED_MACROS,
+ ENDIF_LABELS,
+ ERROR,
// SYSTEM_HEADERS
}
diff --git a/src/resources/log4j.properties b/src/main/resources/log4j.properties
index 901854c..901854c 100644
--- a/src/resources/log4j.properties
+++ b/src/main/resources/log4j.properties
diff --git a/src/resources/org/anarres/cpp/taskdef.properties b/src/main/resources/org/anarres/cpp/taskdef.properties
index 6d575d7..6d575d7 100644
--- a/src/resources/org/anarres/cpp/taskdef.properties
+++ b/src/main/resources/org/anarres/cpp/taskdef.properties
diff --git a/src/java/org/anarres/cpp/Version.java b/src/main/velocity/org/anarres/cpp/Version.java
index 3cc9e47..62494b5 100644
--- a/src/java/org/anarres/cpp/Version.java
+++ b/src/main/velocity/org/anarres/cpp/Version.java
@@ -17,6 +17,8 @@
package org.anarres.cpp;
+import javax.annotation.Nonnull;
+
/**
* System version metadata for Anarres Java C Preprocessor ${version}.
*
@@ -30,17 +32,18 @@ public class Version {
private static final String VERSION = "${version}";
- private static final int major;
- private static final int minor;
- private static final int patch;
+ private static final int major;
+ private static final int minor;
+ private static final int patch;
static {
- String[] tmp = VERSION.split("\\.");
+ String[] tmp = VERSION.split("[\\.-]");
major = Integer.parseInt(tmp[0]);
minor = Integer.parseInt(tmp[1]);
patch = Integer.parseInt(tmp[2]);
}
+ @Nonnull
public static String getVersion() {
return VERSION;
}
diff --git a/src/test/java/org/anarres/cpp/CppReaderTest.java b/src/test/java/org/anarres/cpp/CppReaderTest.java
new file mode 100644
index 0000000..27eba06
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/CppReaderTest.java
@@ -0,0 +1,34 @@
+package org.anarres.cpp;
+
+import java.util.Collections;
+
+import java.io.StringReader;
+import java.io.BufferedReader;
+import org.junit.Test;
+
+public class CppReaderTest {
+
+ private void testCppReader(String in, String out)
+ throws Exception {
+ System.out.println("Testing " + in + " => " + out);
+ StringReader r = new StringReader(in);
+ CppReader p = new CppReader(r);
+ p.getPreprocessor().setSystemIncludePath(
+ Collections.singletonList("src/test/resources")
+ );
+ p.getPreprocessor().getFeatures().add(Feature.LINEMARKERS);
+ BufferedReader b = new BufferedReader(p);
+
+ String line;
+ while ((line = b.readLine()) != null) {
+ System.out.println(" >> " + line);
+ }
+ }
+
+ @Test
+ public void testCppReader()
+ throws Exception {
+ testCppReader("#include <test0.h>\n", "ab");
+ }
+
+}
diff --git a/src/test/java/org/anarres/cpp/ErrorTest.java b/src/test/java/org/anarres/cpp/ErrorTest.java
new file mode 100644
index 0000000..8777452
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/ErrorTest.java
@@ -0,0 +1,65 @@
+package org.anarres.cpp;
+
+import java.io.IOException;
+import org.junit.Test;
+import static org.anarres.cpp.Token.*;
+import static org.junit.Assert.*;
+
+public class ErrorTest {
+
+ private boolean testError(Preprocessor p)
+ throws LexerException,
+ IOException {
+ for (;;) {
+ Token tok = p.token();
+ if (tok.getType() == EOF)
+ break;
+ if (tok.getType() == INVALID)
+ return true;
+ }
+ return false;
+ }
+
+ private void testError(String input) throws Exception {
+ StringLexerSource sl;
+ PreprocessorListener pl;
+ Preprocessor p;
+
+ /* Without a PreprocessorListener, throws an exception. */
+ sl = new StringLexerSource(input, true);
+ p = new Preprocessor();
+ p.addFeature(Feature.CSYNTAX);
+ p.addInput(sl);
+ try {
+ assertTrue(testError(p));
+ fail("Lexing unexpectedly succeeded without listener.");
+ } catch (LexerException e) {
+ /* required */
+ }
+
+ /* With a PreprocessorListener, records the error. */
+ sl = new StringLexerSource(input, true);
+ p = new Preprocessor();
+ p.addFeature(Feature.CSYNTAX);
+ p.addInput(sl);
+ pl = new PreprocessorListener();
+ p.setListener(pl);
+ assertNotNull("CPP has listener", p.getListener());
+ assertTrue(testError(p));
+ assertTrue("Listener has errors", pl.getErrors() > 0);
+
+ /* Without CSYNTAX, works happily. */
+ sl = new StringLexerSource(input, true);
+ p = new Preprocessor();
+ p.addInput(sl);
+ assertTrue(testError(p));
+ }
+
+ @Test
+ public void testErrors() throws Exception {
+ testError("\"");
+ testError("'");
+ // testError("''");
+ }
+
+}
diff --git a/src/test/java/org/anarres/cpp/JavaFileSystemTest.java b/src/test/java/org/anarres/cpp/JavaFileSystemTest.java
new file mode 100644
index 0000000..0ca44be
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/JavaFileSystemTest.java
@@ -0,0 +1,38 @@
+package org.anarres.cpp;
+
+import java.io.FileNotFoundException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class JavaFileSystemTest {
+
+ @Test
+ public void testJavaFileSystem() throws Exception {
+ JavaFileSystem fs = new JavaFileSystem();
+ VirtualFile f;
+
+ /* Anyone who has this file on their Unix box is messed up. */
+ f = fs.getFile("/foo/bar baz");
+ try {
+ f.getSource(); /* drop on floor */
+
+ assertTrue("Got a source for a non-file", f.isFile());
+ } catch (FileNotFoundException e) {
+ assertFalse("Got no source for a file", f.isFile());
+ }
+
+ /* We hope we have this. */
+ f = fs.getFile("/usr/include/stdio.h");
+ try {
+ f.getSource(); /* drop on floor */
+
+ System.out.println("Opened stdio.h");
+ assertTrue("Got a source for a non-file", f.isFile());
+ } catch (FileNotFoundException e) {
+ System.out.println("Failed to open stdio.h");
+ assertFalse("Got no source for a file", f.isFile());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/anarres/cpp/JoinReaderTest.java b/src/test/java/org/anarres/cpp/JoinReaderTest.java
new file mode 100644
index 0000000..527aa81
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/JoinReaderTest.java
@@ -0,0 +1,41 @@
+package org.anarres.cpp;
+
+import java.io.StringReader;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class JoinReaderTest {
+
+ private void testJoinReader(String in, String out, boolean tg)
+ throws Exception {
+ System.out.println("Testing " + in + " => " + out);
+ StringReader r = new StringReader(in);
+ JoinReader j = new JoinReader(r, tg);
+
+ for (int i = 0; i < out.length(); i++) {
+ int c = j.read();
+ System.out.println("At offset " + i + ": " + (char) c);
+ assertEquals((char) out.charAt(i), c);
+ }
+ assertEquals(-1, j.read());
+ assertEquals(-1, j.read());
+ }
+
+ private void testJoinReader(String in, String out)
+ throws Exception {
+ testJoinReader(in, out, true);
+ testJoinReader(in, out, false);
+ }
+
+ @Test
+ public void testJoinReader()
+ throws Exception {
+ testJoinReader("ab", "ab");
+ testJoinReader("a\\b", "a\\b");
+ testJoinReader("a\nb", "a\nb");
+ testJoinReader("a\\\nb", "ab\n");
+ testJoinReader("foo??(bar", "foo[bar", true);
+ testJoinReader("foo??/\nbar", "foobar\n", true);
+ }
+
+}
diff --git a/src/test/java/org/anarres/cpp/LexerSourceTest.java b/src/test/java/org/anarres/cpp/LexerSourceTest.java
new file mode 100644
index 0000000..76bc673
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/LexerSourceTest.java
@@ -0,0 +1,86 @@
+package org.anarres.cpp;
+
+import java.util.Arrays;
+import org.junit.Test;
+import static org.anarres.cpp.Token.*;
+import static org.junit.Assert.*;
+
+public class LexerSourceTest {
+
+ private void testLexerSource(String in, int... out)
+ throws Exception {
+ System.out.println("Testing '" + in + "' => "
+ + Arrays.toString(out));
+ StringLexerSource s = new StringLexerSource(in);
+
+ int col = 0;
+ for (int i = 0; i < out.length; i++) {
+ Token tok = s.token();
+ System.out.println("Token is " + tok);
+ assertEquals(out[i], tok.getType());
+ // assertEquals(col, tok.getColumn());
+ col += tok.getText().length();
+ }
+
+ Token tok = s.token();
+ System.out.println("Token is " + tok);
+ assertEquals(EOF, tok.getType());
+ }
+
+ @Test
+ public void testLexerSource()
+ throws Exception {
+
+ testLexerSource("int a = 5;",
+ IDENTIFIER, WHITESPACE, IDENTIFIER, WHITESPACE,
+ '=', WHITESPACE, NUMBER, ';', EOF
+ );
+
+ // \n is WHITESPACE because ppvalid = false
+ testLexerSource("# # \r\n\n\r \rfoo",
+ HASH, WHITESPACE, '#', WHITESPACE, IDENTIFIER
+ );
+
+ testLexerSource("%:%:", PASTE);
+ testLexerSource("%:?", '#', '?');
+ testLexerSource("%:%=", '#', MOD_EQ);
+ testLexerSource("0x1234ffdUL 0765I",
+ NUMBER, WHITESPACE, NUMBER);
+
+ testLexerSource("+= -= *= /= %= <= >= >>= <<= &= |= ^= x",
+ PLUS_EQ, WHITESPACE,
+ SUB_EQ, WHITESPACE,
+ MULT_EQ, WHITESPACE,
+ DIV_EQ, WHITESPACE,
+ MOD_EQ, WHITESPACE,
+ LE, WHITESPACE,
+ GE, WHITESPACE,
+ RSH_EQ, WHITESPACE,
+ LSH_EQ, WHITESPACE,
+ AND_EQ, WHITESPACE,
+ OR_EQ, WHITESPACE,
+ XOR_EQ, WHITESPACE,
+ IDENTIFIER);
+
+ testLexerSource("/**/", CCOMMENT);
+ testLexerSource("/* /**/ */", CCOMMENT, WHITESPACE, '*', '/');
+ testLexerSource("/** ** **/", CCOMMENT);
+ testLexerSource("//* ** **/", CPPCOMMENT);
+ testLexerSource("'\\r' '\\xf' '\\xff' 'x' 'aa' ''",
+ CHARACTER, WHITESPACE,
+ CHARACTER, WHITESPACE,
+ CHARACTER, WHITESPACE,
+ CHARACTER, WHITESPACE,
+ SQSTRING, WHITESPACE,
+ SQSTRING);
+
+ testLexerSource("1i1I1l1L1ui1ul",
+ NUMBER, NUMBER,
+ NUMBER, NUMBER,
+ NUMBER, NUMBER);
+
+ testLexerSource("'' 'x' 'xx'",
+ SQSTRING, WHITESPACE, CHARACTER, WHITESPACE, SQSTRING);
+ }
+
+}
diff --git a/src/test/java/org/anarres/cpp/MainTest.java b/src/test/java/org/anarres/cpp/MainTest.java
new file mode 100644
index 0000000..5ff7350
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/MainTest.java
@@ -0,0 +1,11 @@
+package org.anarres.cpp;
+
+import org.junit.Test;
+
+public class MainTest {
+
+ @Test
+ public void testMain() throws Exception {
+ Main.main(new String[]{"--version"});
+ }
+}
diff --git a/src/test/java/org/anarres/cpp/PreprocessorTest.java b/src/test/java/org/anarres/cpp/PreprocessorTest.java
new file mode 100644
index 0000000..fb2a8ac
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/PreprocessorTest.java
@@ -0,0 +1,167 @@
+package org.anarres.cpp;
+
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import org.junit.Before;
+import org.junit.Test;
+import static org.anarres.cpp.Token.*;
+import static org.junit.Assert.*;
+
+public class PreprocessorTest {
+
+ private OutputStreamWriter writer;
+ private Preprocessor p;
+
+ @Before
+ public void setUp() throws Exception {
+ final PipedOutputStream po = new PipedOutputStream();
+ writer = new OutputStreamWriter(po);
+
+ p = new Preprocessor();
+ p.addInput(
+ new LexerSource(
+ new InputStreamReader(
+ new PipedInputStream(po)
+ ),
+ true
+ )
+ );
+ }
+
+ private static class I {
+
+ private String t;
+
+ public I(String t) {
+ this.t = t;
+ }
+
+ public String getText() {
+ return t;
+ }
+
+ public String toString() {
+ return getText();
+ }
+ }
+
+ private static I I(String t) {
+ return new I(t);
+ }
+
+ /*
+ * When writing tests in this file, remember the preprocessor
+ * stashes NLs, so you won't see an immediate NL at the end of any
+ * input line. You will see it right before the next nonblank on
+ * the following input line.
+ */
+ @Test
+ public void testPreprocessor() throws Exception {
+ /* Magic macros */
+ testInput("line = __LINE__\n",
+ I("line"), WHITESPACE, '=', WHITESPACE, NUMBER
+ /*, NL - all nls deferred so as not to block the reader */
+ );
+ testInput("file = __FILE__\n", NL, /* from before, etc */
+ I("file"), WHITESPACE, '=', WHITESPACE, STRING
+ );
+
+ /* Simple definitions */
+ testInput("#define A a /* a defined */\n", NL);
+ testInput("#define B b /* b defined */\n", NL);
+ testInput("#define C c /* c defined */\n", NL);
+
+ /* Expansion of arguments */
+ testInput("#define EXPAND(x) x\n", NL);
+ testInput("EXPAND(a)\n", NL, I("a"));
+ testInput("EXPAND(A)\n", NL, I("a"));
+
+ /* Stringification */
+ testInput("#define _STRINGIFY(x) #x\n", NL);
+ testInput("_STRINGIFY(A)\n", NL, "A");
+ testInput("#define STRINGIFY(x) _STRINGIFY(x)\n", NL);
+ testInput("STRINGIFY(b)\n", NL, "b");
+ testInput("STRINGIFY(A)\n", NL, "a");
+
+ /* Concatenation */
+ testInput("#define _CONCAT(x, y) x ## y\n", NL);
+ testInput("_CONCAT(A, B)\n", NL, I("AB"));
+ testInput("#define A_CONCAT done_a_concat\n", NL);
+ testInput("_CONCAT(A, _CONCAT(B, C))\n", NL,
+ I("done_a_concat"), '(', I("b"), ',', WHITESPACE, I("c"), ')'
+ );
+ testInput("#define CONCAT(x, y) _CONCAT(x, y)\n", NL);
+ testInput("CONCAT(A, CONCAT(B, C))\n", NL, I("abc"));
+ testInput("#define _CONCAT3(x, y, z) x ## y ## z\n", NL);
+ testInput("_CONCAT3(a, b, c)\n", NL, I("abc"));
+ testInput("_CONCAT3(A, B, C)\n", NL, I("ABC"));
+
+ /* Redefinitions, undefinitions. */
+ testInput("#define two three\n", NL);
+ testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT);
+ testInput("#define one two\n", NL);
+ testInput("one /* three */\n", NL, I("three"), WHITESPACE, CCOMMENT);
+ testInput("#undef two\n", NL);
+ testInput("#define two five\n", NL);
+ testInput("one /* five */\n", NL, I("five"), WHITESPACE, CCOMMENT);
+ testInput("#undef two\n", NL);
+ testInput("one /* two */\n", NL, I("two"), WHITESPACE, CCOMMENT);
+ testInput("#undef one\n", NL);
+ testInput("#define one four\n", NL);
+ testInput("one /* four */\n", NL, I("four"), WHITESPACE, CCOMMENT);
+ testInput("#undef one\n", NL);
+ testInput("#define one one\n", NL);
+ testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT);
+
+ /* Variadic macros. */
+ testInput("#define var(x...) a x b\n", NL);
+ testInput("var(e, f, g)\n", NL,
+ I("a"), WHITESPACE,
+ I("e"), ',', WHITESPACE,
+ I("f"), ',', WHITESPACE,
+ I("g"), WHITESPACE,
+ I("b")
+ );
+
+ testInput("#define _Widen(x) L ## x\n", NL);
+ testInput("#define Widen(x) _Widen(x)\n", NL);
+ testInput("#define LStr(x) _Widen(#x)\n", NL);
+ testInput("LStr(x);\n", NL, I("L"), "x");
+
+ writer.close();
+
+ Token t;
+ do {
+ t = p.token();
+ System.out.println("Remaining token " + t);
+ } while (t.getType() != EOF);
+ }
+
+ private void testInput(String in, Object... out)
+ throws Exception {
+ System.out.print("Input: " + in);
+ writer.write(in);
+ writer.flush();
+ for (int i = 0; i < out.length; i++) {
+ Token t = p.token();
+ System.out.println(t);
+ Object v = out[i];
+ if (v instanceof String) {
+ if (t.getType() != STRING)
+ fail("Expected STRING, but got " + t);
+ assertEquals((String) v, (String) t.getValue());
+ } else if (v instanceof I) {
+ if (t.getType() != IDENTIFIER)
+ fail("Expected IDENTIFIER " + v + ", but got " + t);
+ assertEquals(((I) v).getText(), (String) t.getText());
+ } else if (v instanceof Character)
+ assertEquals((int) ((Character) v).charValue(), t.getType());
+ else if (v instanceof Integer)
+ assertEquals(((Integer) v).intValue(), t.getType());
+ else
+ fail("Bad object " + v.getClass());
+ }
+ }
+}
diff --git a/src/input/test0.c b/src/test/resources/test0.c
index 7e91637..7e91637 100644
--- a/src/input/test0.c
+++ b/src/test/resources/test0.c
diff --git a/src/input/test0.h b/src/test/resources/test0.h
index b6697c6..b6697c6 100644
--- a/src/input/test0.h
+++ b/src/test/resources/test0.h
diff --git a/src/input/test1.c b/src/test/resources/test1.c
index 3e6fbda..3e6fbda 100644
--- a/src/input/test1.c
+++ b/src/test/resources/test1.c
diff --git a/src/input/test1.h b/src/test/resources/test1.h
index 0b690f7..0b690f7 100644
--- a/src/input/test1.h
+++ b/src/test/resources/test1.h
diff --git a/src/input/trigraph.c b/src/test/resources/trigraph.c
index 89615fe..89615fe 100644
--- a/src/input/trigraph.c
+++ b/src/test/resources/trigraph.c
diff --git a/src/tests/AutoTestSuite.java b/src/tests/AutoTestSuite.java
deleted file mode 100644
index 894a365..0000000
--- a/src/tests/AutoTestSuite.java
+++ /dev/null
@@ -1,121 +0,0 @@
-import java.lang.reflect.Modifier;
-
-import java.io.File;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import junit.framework.TestSuite;
-import junit.framework.TestCase;
-import junit.framework.Test;
-
-public class AutoTestSuite extends TestSuite {
- private String testPackage;
- private Set<String> testCases;
- private boolean testAll;
- private File root;
-
- public AutoTestSuite() {
- this.testPackage = System.getProperty("test.package");
- String tcase = System.getProperty("test.case");
- if (tcase != null && tcase.length() > 0) {
- this.testCases = new HashSet(Arrays.asList(
- tcase.split("[,:]")
- ));
- }
- else {
- this.testCases = null;
- }
- this.testAll = System.getProperty("test.all") != null;
- this.root = new File(System.getProperty("test.root"));
-
- Set<Class> tests = new HashSet();
-
- findClasses("", root, tests);
-
- Iterator<Class> i = tests.iterator();
-
- while(i.hasNext()) {
- addTestSuite(i.next());
- }
- }
-
- public void addTestSuite(Class clazz) {
- if (testPackage != null) {
- String name = clazz.getPackage().getName();
- if (!name.startsWith(testPackage)) {
- /*
- System.out.println("Skipping test in package '" +
- name + "' - does not start with '" +
- testPackage + "'");
- */
- return;
- }
- }
- if (testCases != null) {
- String name = clazz.getName();
- name = name.substring(name.lastIndexOf('.') + 1);
- if (!testCases.contains(name)) {
- /*
- System.out.println("Skipping test in class '" +
- name + "' - does not start with '" +
- testCases + "'");
- */
- return;
- }
- }
- /*
- if (
- testCases == null &&
- testPackage == null &&
- !testAll &&
- Optional.class.isAssignableFrom(clazz)
- )
- {
- return;
- }
- */
- System.out.println("Adding test class '" + clazz + "'");
- super.addTestSuite(clazz);
- }
-
- public static Test suite() {
- return new AutoTestSuite();
- }
-
- private final void findClasses(String pkg, File root, Set<Class> result) {
- File[] children = root.listFiles();
- for(int i = 0; i<children.length; i++) {
- File child = children[i];
- if(child.isDirectory()) {
- findClasses(
- pkg + child.getName() + ".",
- child,
- result
- );
- } else if(child.isFile()) {
- String name = child.getName();
- // System.out.println("Checking: " + pkg + name);
- if(name.endsWith(".class") && name.indexOf('$') == -1) {
- try {
- Class test = Class.forName(pkg +
- name.substring(0,name.length() - 6));
- int modifiers = test.getModifiers();
- if(
- (modifiers & Modifier.ABSTRACT) > 0 ||
- (modifiers & Modifier.INTERFACE) > 0 ||
- !TestCase.class.isAssignableFrom(test) ||
- TestSuite.class.isAssignableFrom(test)
- )
- continue;
- result.add(test);
- } catch (ClassNotFoundException cnfe) {
- cnfe.printStackTrace();
- }
- }
- }
- }
- }
-}
diff --git a/src/tests/org/anarres/cpp/BaseTestCase.java b/src/tests/org/anarres/cpp/BaseTestCase.java
deleted file mode 100644
index ad6ae6a..0000000
--- a/src/tests/org/anarres/cpp/BaseTestCase.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.anarres.cpp;
-
-import junit.framework.TestCase;
-
-public abstract class BaseTestCase extends TestCase {
-}
diff --git a/src/tests/org/anarres/cpp/CppReaderTestCase.java b/src/tests/org/anarres/cpp/CppReaderTestCase.java
deleted file mode 100644
index df3aeb5..0000000
--- a/src/tests/org/anarres/cpp/CppReaderTestCase.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.anarres.cpp;
-
-import java.util.Collections;
-
-import java.io.StringReader;
-import java.io.BufferedReader;
-
-import junit.framework.Test;
-
-public class CppReaderTestCase extends BaseTestCase implements Test {
-
- private void testCppReader(String in, String out)
- throws Exception {
- System.out.println("Testing " + in + " => " + out);
- StringReader r = new StringReader(in);
- CppReader p = new CppReader(r);
- p.getPreprocessor().setSystemIncludePath(
- Collections.singletonList("src/input")
- );
- p.getPreprocessor().getFeatures().add(Feature.LINEMARKERS);
- BufferedReader b = new BufferedReader(p);
-
- String line;
- while ((line = b.readLine()) != null) {
- System.out.println(" >> " + line);
- }
- }
-
- public void testCppReader()
- throws Exception {
- testCppReader("#include <test0.h>\n", "ab");
- }
-
-}
diff --git a/src/tests/org/anarres/cpp/ErrorTestCase.java b/src/tests/org/anarres/cpp/ErrorTestCase.java
deleted file mode 100644
index cec1dc7..0000000
--- a/src/tests/org/anarres/cpp/ErrorTestCase.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.anarres.cpp;
-
-import java.io.*;
-
-import junit.framework.Test;
-
-import static org.anarres.cpp.Token.*;
-
-public class ErrorTestCase extends BaseTestCase {
-
- private boolean testError(Preprocessor p)
- throws LexerException,
- IOException {
- for (;;) {
- Token tok = p.token();
- if (tok.getType() == EOF)
- break;
- if (tok.getType() == INVALID)
- return true;
- }
- return false;
- }
-
- private void testError(String input) throws Exception {
- StringLexerSource sl;
- PreprocessorListener pl;
- Preprocessor p;
-
- /* Without a PreprocessorListener, throws an exception. */
- sl = new StringLexerSource(input, true);
- p = new Preprocessor();
- p.addFeature(Feature.CSYNTAX);
- p.addInput(sl);
- try {
- assertTrue(testError(p));
- fail("Lexing unexpectedly succeeded without listener.");
- }
- catch (LexerException e) {
- /* required */
- }
-
- /* With a PreprocessorListener, records the error. */
- sl = new StringLexerSource(input, true);
- p = new Preprocessor();
- p.addFeature(Feature.CSYNTAX);
- p.addInput(sl);
- pl = new PreprocessorListener();
- p.setListener(pl);
- assertNotNull("CPP has listener", p.getListener());
- assertTrue(testError(p));
- assertTrue("Listener has errors", pl.getErrors() > 0);
-
- /* Without CSYNTAX, works happily. */
- sl = new StringLexerSource(input, true);
- p = new Preprocessor();
- p.addInput(sl);
- assertTrue(testError(p));
- }
-
- public void testErrors() throws Exception {
- testError("\"");
- testError("'");
- // testError("''");
- }
-
-}
diff --git a/src/tests/org/anarres/cpp/JavaFileSystemTestCase.java b/src/tests/org/anarres/cpp/JavaFileSystemTestCase.java
deleted file mode 100644
index 4f68d68..0000000
--- a/src/tests/org/anarres/cpp/JavaFileSystemTestCase.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.anarres.cpp;
-
-import java.io.*;
-
-import junit.framework.Test;
-
-import static org.anarres.cpp.Token.*;
-
-public class JavaFileSystemTestCase extends BaseTestCase {
-
- public void testJavaFileSystem() throws Exception {
- JavaFileSystem fs = new JavaFileSystem();
- VirtualFile f;
-
- /* Anyone who has this file on their Unix box is messed up. */
- f = fs.getFile("/foo/bar baz");
- try {
- f.getSource(); /* drop on floor */
- assertTrue("Got a source for a non-file", f.isFile());
- }
- catch (FileNotFoundException e) {
- assertFalse("Got no source for a file", f.isFile());
- }
-
- /* We hope we have this. */
- f = fs.getFile("/usr/include/stdio.h");
- try {
- f.getSource(); /* drop on floor */
- System.out.println("Opened stdio.h");
- assertTrue("Got a source for a non-file", f.isFile());
- }
- catch (FileNotFoundException e) {
- System.out.println("Failed to open stdio.h");
- assertFalse("Got no source for a file", f.isFile());
- }
-
- }
-
-}
diff --git a/src/tests/org/anarres/cpp/JoinReaderTestCase.java b/src/tests/org/anarres/cpp/JoinReaderTestCase.java
deleted file mode 100644
index 6c11449..0000000
--- a/src/tests/org/anarres/cpp/JoinReaderTestCase.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.anarres.cpp;
-
-import java.io.StringReader;
-
-import junit.framework.Test;
-
-public class JoinReaderTestCase extends BaseTestCase implements Test {
-
- private void testJoinReader(String in, String out, boolean tg)
- throws Exception {
- System.out.println("Testing " + in + " => " + out);
- StringReader r = new StringReader(in);
- JoinReader j = new JoinReader(r, tg);
-
- for (int i = 0; i < out.length(); i++) {
- int c = j.read();
- System.out.println("At offset " + i + ": " + (char)c);
- assertEquals((char)out.charAt(i), c);
- }
- assertEquals(-1, j.read());
- assertEquals(-1, j.read());
- }
-
- private void testJoinReader(String in, String out)
- throws Exception {
- testJoinReader(in, out, true);
- testJoinReader(in, out, false);
- }
-
- public void testJoinReader()
- throws Exception {
- testJoinReader("ab", "ab");
- testJoinReader("a\\b", "a\\b");
- testJoinReader("a\nb", "a\nb");
- testJoinReader("a\\\nb", "ab\n");
- testJoinReader("foo??(bar", "foo[bar", true);
- testJoinReader("foo??/\nbar", "foobar\n", true);
- }
-
-}
diff --git a/src/tests/org/anarres/cpp/LexerSourceTestCase.java b/src/tests/org/anarres/cpp/LexerSourceTestCase.java
deleted file mode 100644
index d554814..0000000
--- a/src/tests/org/anarres/cpp/LexerSourceTestCase.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package org.anarres.cpp;
-
-import java.io.StringReader;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import junit.framework.Test;
-
-import static org.anarres.cpp.Token.*;
-
-public class LexerSourceTestCase extends BaseTestCase implements Test {
-
- private void testLexerSource(String in, int... out)
- throws Exception {
- System.out.println("Testing '" + in + "' => " +
- Arrays.toString(out));
- StringLexerSource s = new StringLexerSource(in);
-
- int col = 0;
- for (int i = 0; i < out.length; i++) {
- Token tok = s.token();
- System.out.println("Token is " + tok);
- assertEquals(out[i], tok.getType());
- assertEquals(col, tok.getColumn());
- col += tok.getText().length();
- }
- assertEquals(EOF, s.token().getType());
- }
-
- public void testLexerSource()
- throws Exception {
-
- testLexerSource("int a = 5;",
- IDENTIFIER, WHITESPACE, IDENTIFIER, WHITESPACE,
- '=', WHITESPACE, NUMBER, ';', EOF
- );
-
- // \n is WHITESPACE because ppvalid = false
- testLexerSource("# # \r\n\n\r \rfoo",
- HASH, WHITESPACE, '#', WHITESPACE, IDENTIFIER
- );
-
- testLexerSource("%:%:", PASTE);
- testLexerSource("%:?", '#', '?');
- testLexerSource("%:%=", '#', MOD_EQ);
- testLexerSource("0x1234ffdUL 0765I",
- NUMBER, WHITESPACE, NUMBER);
-
- testLexerSource("+= -= *= /= %= <= >= >>= <<= &= |= ^= x",
- PLUS_EQ, WHITESPACE,
- SUB_EQ, WHITESPACE,
- MULT_EQ, WHITESPACE,
- DIV_EQ, WHITESPACE,
- MOD_EQ, WHITESPACE,
- LE, WHITESPACE,
- GE, WHITESPACE,
- RSH_EQ, WHITESPACE,
- LSH_EQ, WHITESPACE,
- AND_EQ, WHITESPACE,
- OR_EQ, WHITESPACE,
- XOR_EQ, WHITESPACE,
- IDENTIFIER);
-
- testLexerSource("/**/", CCOMMENT);
- testLexerSource("/* /**/ */", CCOMMENT, WHITESPACE, '*', '/');
- testLexerSource("/** ** **/", CCOMMENT);
- testLexerSource("//* ** **/", CPPCOMMENT);
- testLexerSource("'\\r' '\\xf' '\\xff' 'x' 'aa' ''",
- CHARACTER, WHITESPACE,
- CHARACTER, WHITESPACE,
- CHARACTER, WHITESPACE,
- CHARACTER, WHITESPACE,
- SQSTRING, WHITESPACE,
- SQSTRING);
-
- testLexerSource("1i1I1l1L1ui1ul",
- NUMBER, NUMBER,
- NUMBER, NUMBER,
- NUMBER, NUMBER);
-
- testLexerSource("'' 'x' 'xx'",
- SQSTRING, WHITESPACE, CHARACTER, WHITESPACE, SQSTRING);
- }
-
-}
diff --git a/src/tests/org/anarres/cpp/MainTestCase.java b/src/tests/org/anarres/cpp/MainTestCase.java
deleted file mode 100644
index 313a463..0000000
--- a/src/tests/org/anarres/cpp/MainTestCase.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.anarres.cpp;
-
-import java.io.*;
-
-import junit.framework.Test;
-
-import static org.anarres.cpp.Token.*;
-
-public class MainTestCase extends BaseTestCase {
-
- public void testMain() throws Exception {
- Main.main(new String[] { "--version" });
- }
-
-}
diff --git a/src/tests/org/anarres/cpp/PreprocessorTestCase.java b/src/tests/org/anarres/cpp/PreprocessorTestCase.java
deleted file mode 100644
index 217659a..0000000
--- a/src/tests/org/anarres/cpp/PreprocessorTestCase.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package org.anarres.cpp;
-
-import java.io.*;
-
-import junit.framework.Test;
-
-import static org.anarres.cpp.Token.*;
-
-public class PreprocessorTestCase extends BaseTestCase {
- private OutputStreamWriter writer;
- private Preprocessor p;
-
- public void setUp() throws Exception {
- final PipedOutputStream po = new PipedOutputStream();
- writer = new OutputStreamWriter(po);
-
- p = new Preprocessor();
- p.addInput(
- new LexerSource(
- new InputStreamReader(
- new PipedInputStream(po)
- ),
- true
- )
- );
- }
-
- private static class I {
- private String t;
- public I(String t) {
- this.t = t;
- }
- public String getText() {
- return t;
- }
- public String toString() {
- return getText();
- }
- }
-
- private static I I(String t) {
- return new I(t);
- }
-
-/*
- * When writing tests in this file, remember the preprocessor
- * stashes NLs, so you won't see an immediate NL at the end of any
- * input line. You will see it right before the next nonblank on
- * the following input line.
- */
-
- public void testPreprocessor() throws Exception {
- /* Magic macros */
- testInput("line = __LINE__\n",
- I("line"), WHITESPACE, '=', WHITESPACE, NUMBER
- /*, NL - all nls deferred so as not to block the reader */
- );
- testInput("file = __FILE__\n", NL, /* from before, etc */
- I("file"), WHITESPACE, '=', WHITESPACE, STRING
- );
-
- /* Simple definitions */
- testInput("#define A a /* a defined */\n", NL);
- testInput("#define B b /* b defined */\n", NL);
- testInput("#define C c /* c defined */\n", NL);
-
- /* Expansion of arguments */
- testInput("#define EXPAND(x) x\n", NL);
- testInput("EXPAND(a)\n", NL, I("a"));
- testInput("EXPAND(A)\n", NL, I("a"));
-
- /* Stringification */
- testInput("#define _STRINGIFY(x) #x\n", NL);
- testInput("_STRINGIFY(A)\n", NL, "A");
- testInput("#define STRINGIFY(x) _STRINGIFY(x)\n", NL);
- testInput("STRINGIFY(b)\n", NL, "b");
- testInput("STRINGIFY(A)\n", NL, "a");
-
- /* Concatenation */
- testInput("#define _CONCAT(x, y) x ## y\n", NL);
- testInput("_CONCAT(A, B)\n", NL, I("AB"));
- testInput("#define A_CONCAT done_a_concat\n", NL);
- testInput("_CONCAT(A, _CONCAT(B, C))\n", NL,
- I("done_a_concat"), '(', I("b"), ',', WHITESPACE, I("c"), ')'
- );
- testInput("#define CONCAT(x, y) _CONCAT(x, y)\n", NL);
- testInput("CONCAT(A, CONCAT(B, C))\n", NL, I("abc"));
- testInput("#define _CONCAT3(x, y, z) x ## y ## z\n", NL);
- testInput("_CONCAT3(a, b, c)\n", NL, I("abc"));
- testInput("_CONCAT3(A, B, C)\n", NL, I("ABC"));
-
-/* Redefinitions, undefinitions. */
-testInput("#define two three\n", NL);
-testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT);
-testInput("#define one two\n", NL);
-testInput("one /* three */\n", NL, I("three"), WHITESPACE, CCOMMENT);
-testInput("#undef two\n", NL);
-testInput("#define two five\n", NL);
-testInput("one /* five */\n", NL, I("five"), WHITESPACE, CCOMMENT);
-testInput("#undef two\n", NL);
-testInput("one /* two */\n", NL, I("two"), WHITESPACE, CCOMMENT);
-testInput("#undef one\n", NL);
-testInput("#define one four\n", NL);
-testInput("one /* four */\n", NL, I("four"), WHITESPACE, CCOMMENT);
-testInput("#undef one\n", NL);
-testInput("#define one one\n", NL);
-testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT);
-
- /* Variadic macros. */
- testInput("#define var(x...) a x b\n", NL);
- testInput("var(e, f, g)\n", NL,
- I("a"), WHITESPACE,
- I("e"), ',', WHITESPACE,
- I("f"), ',', WHITESPACE,
- I("g"), WHITESPACE,
- I("b")
- );
-
- testInput("#define _Widen(x) L ## x\n", NL);
- testInput("#define Widen(x) _Widen(x)\n", NL);
- testInput("#define LStr(x) _Widen(#x)\n", NL);
- testInput("LStr(x);\n", NL, I("L"), "x");
-
- writer.close();
-
- Token t;
- do {
- t = p.token();
- System.out.println("Remaining token " + t);
- } while(t.getType() != EOF);
- }
-
- private void testInput(String in, Object... out)
- throws Exception {
- System.out.print("Input: " + in);
- writer.write(in);
- writer.flush();
- for (int i = 0; i < out.length; i++) {
- Token t = p.token();
- System.out.println(t);
- Object v = out[i];
- if (v instanceof String) {
- if (t.getType() != STRING)
- fail("Expected STRING, but got " + t);
- assertEquals((String)v, (String)t.getValue());
- }
- else if (v instanceof I) {
- if (t.getType() != IDENTIFIER)
- fail("Expected IDENTIFIER " + v + ", but got " + t);
- assertEquals( ((I)v).getText(), (String)t.getText());
- }
- else if (v instanceof Character)
- assertEquals( (int)((Character)v).charValue(), t.getType());
- else if (v instanceof Integer)
- assertEquals( ((Integer)v).intValue(), t.getType());
- else
- fail("Bad object " + v.getClass());
- }
- }
-}