aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShevek <[email protected]>2013-12-28 04:56:55 -0800
committerShevek <[email protected]>2013-12-28 04:56:55 -0800
commitefc10c0f0bc0e8c245282fc55d28adb2ce1967eb (patch)
tree0b10b654032bf4780295de5aa37df03c6c53f04c /src
parentc6569cd5635d97da75faa8ce6b4772223ff6201c (diff)
Fix #pragma once.
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/anarres/cpp/Feature.java5
-rw-r--r--src/main/java/org/anarres/cpp/FileLexerSource.java94
-rw-r--r--src/main/java/org/anarres/cpp/Preprocessor.java90
-rw-r--r--src/test/java/org/anarres/cpp/CppReaderTest.java29
-rw-r--r--src/test/resources/once.c2
-rw-r--r--src/test/resources/once.h2
6 files changed, 145 insertions, 77 deletions
diff --git a/src/main/java/org/anarres/cpp/Feature.java b/src/main/java/org/anarres/cpp/Feature.java
index 04a68b7..e119005 100644
--- a/src/main/java/org/anarres/cpp/Feature.java
+++ b/src/main/java/org/anarres/cpp/Feature.java
@@ -38,5 +38,8 @@ public enum Feature {
/** Supports lexing of objective-C. */
OBJCSYNTAX,
- INCLUDENEXT
+ INCLUDENEXT,
+
+ /** Random extensions. */
+ PRAGMA_ONCE
}
diff --git a/src/main/java/org/anarres/cpp/FileLexerSource.java b/src/main/java/org/anarres/cpp/FileLexerSource.java
index db5f9e0..1505506 100644
--- a/src/main/java/org/anarres/cpp/FileLexerSource.java
+++ b/src/main/java/org/anarres/cpp/FileLexerSource.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,10 +21,7 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import java.util.List;
-import java.util.Iterator;
-
-import static org.anarres.cpp.Token.*;
+import javax.annotation.Nonnull;
/**
* A {@link Source} which lexes a file.
@@ -35,50 +31,60 @@ import static org.anarres.cpp.Token.*;
* @see Source
*/
public class FileLexerSource extends LexerSource {
- // private File file;
- private String path;
- /**
- * Creates a new Source for lexing the given File.
- *
- * Preprocessor directives are honoured within the file.
- */
- public FileLexerSource(File file, String path)
- throws IOException {
- super(
- new BufferedReader(
- new FileReader(
- file
- )
- ),
- true
- );
+ private final String path;
+ private final File file;
+
+ /**
+ * Creates a new Source for lexing the given File.
+ *
+ * Preprocessor directives are honoured within the file.
+ */
+ public FileLexerSource(@Nonnull File file, String path)
+ throws IOException {
+ super(
+ new BufferedReader(
+ new FileReader(
+ file
+ )
+ ),
+ true
+ );
+
+ this.file = file;
+ this.path = path;
+ }
- // this.file = file;
- this.path = path;
- }
+ public FileLexerSource(@Nonnull File file)
+ throws IOException {
+ this(file, file.getPath());
+ }
- public FileLexerSource(File file)
- throws IOException {
- this(file, file.getPath());
- }
+ public FileLexerSource(@Nonnull String path)
+ throws IOException {
+ this(new File(path), path);
+ }
- public FileLexerSource(String path)
- throws IOException {
- this(new File(path));
- }
+ @Nonnull
+ public File getFile() {
+ return file;
+ }
- @Override
- /* pp */ String getPath() {
- return path;
- }
+ /**
+ * This is not necessarily the same as getFile().getPath() in case we are in a chroot.
+ */
+ @Override
+ /* pp */ String getPath() {
+ return path;
+ }
- @Override
- /* pp */ String getName() {
- return getPath();
- }
+ @Override
+ /* pp */ String getName() {
+ return getPath();
+ }
- public String toString() {
- return "file " + path;
- }
+ @Override
+ public String toString() {
+ return "file " + getPath();
+ }
}
diff --git a/src/main/java/org/anarres/cpp/Preprocessor.java b/src/main/java/org/anarres/cpp/Preprocessor.java
index 4d9cb4f..f636872 100644
--- a/src/main/java/org/anarres/cpp/Preprocessor.java
+++ b/src/main/java/org/anarres/cpp/Preprocessor.java
@@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -96,15 +97,16 @@ public class Preprocessor implements Closeable {
private static final Macro __FILE__ = new Macro(INTERNAL, "__FILE__");
private static final Macro __COUNTER__ = new Macro(INTERNAL, "__COUNTER__");
- private List<Source> inputs;
+ private final List<Source> inputs;
/* The fundamental engine. */
- private Map<String, Macro> macros;
- private Stack<State> states;
+ private final Map<String, Macro> macros;
+ private final Stack<State> states;
private Source source;
/* Miscellaneous support. */
private int counter;
+ private final Set<String> onceseenpaths = new HashSet<String>();
/* Support junk to make it work like cpp */
private List<String> quoteincludepath; /* -iquote */
@@ -112,8 +114,8 @@ public class Preprocessor implements Closeable {
private List<String> sysincludepath; /* -I */
private List<String> frameworkspath;
- private Set<Feature> features;
- private Set<Warning> warnings;
+ private final Set<Feature> features;
+ private final Set<Warning> warnings;
private VirtualFileSystem filesystem;
private PreprocessorListener listener;
@@ -218,6 +220,13 @@ public class Preprocessor implements Closeable {
}
/**
+ * Adds features to the feature-set of this Preprocessor.
+ */
+ public void addFeatures(Feature... f) {
+ addFeatures(Arrays.asList(f));
+ }
+
+ /**
* Returns true if the given feature is in
* the feature-set of this Preprocessor.
*/
@@ -508,7 +517,8 @@ public class Preprocessor implements Closeable {
* @see #getSource()
* @see #push_source(Source,boolean)
*/
- protected void pop_source()
+ @CheckForNull
+ protected Token pop_source(boolean linemarker)
throws IOException {
if (listener != null)
listener.handleSourceChange(this.source, "pop");
@@ -518,14 +528,31 @@ public class Preprocessor implements Closeable {
s.close();
if (listener != null && this.source != null)
listener.handleSourceChange(this.source, "resume");
+
+ 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 null;
}
+ protected void pop_source()
+ throws IOException {
+ pop_source(false);
+ }
/* Source tokens */
private Token source_token;
/* XXX Make this include the NL, and make all cpp directives eat
* their own NL. */
+ @Nonnull
private Token line_token(int line, String name, String extra) {
StringBuilder buf = new StringBuilder();
buf.append("#line ").append(line)
@@ -536,6 +563,7 @@ public class Preprocessor implements Closeable {
return new Token(P_LINE, line, 0, buf.toString(), null);
}
+ @Nonnull
private Token source_token()
throws IOException,
LexerException {
@@ -562,16 +590,9 @@ public class Preprocessor implements Closeable {
/* 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");
- }
+ Token mark = pop_source(true);
+ if (mark != null)
+ return mark;
continue;
}
if (getFeature(Feature.DEBUG))
@@ -620,16 +641,9 @@ public class Preprocessor implements Closeable {
/* 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");
- }
+ Token mark = pop_source(true);
+ if (mark != null)
+ return mark;
}
return tok;
}
@@ -823,7 +837,8 @@ public class Preprocessor implements Closeable {
* Expands an argument.
*/
/* I'd rather this were done lazily, but doing so breaks spec. */
- /* pp */ List<Token> expand(List<Token> arg)
+ @Nonnull
+ /* pp */ List<Token> expand(@Nonnull List<Token> arg)
throws IOException,
LexerException {
List<Token> expansion = new ArrayList<Token>();
@@ -853,7 +868,8 @@ public class Preprocessor implements Closeable {
}
}
- pop_source();
+ // Always returns null.
+ pop_source(false);
return expansion;
}
@@ -1026,6 +1042,7 @@ public class Preprocessor implements Closeable {
}
+ @Nonnull
private Token undef()
throws IOException,
LexerException {
@@ -1175,9 +1192,26 @@ public class Preprocessor implements Closeable {
}
}
+ protected void pragma_once(@Nonnull Token name)
+ throws IOException, LexerException {
+ Source s = this.source;
+ if (!onceseenpaths.add(s.getPath())) {
+ Token mark = pop_source(true);
+ // FixedTokenSource should never generate a linemarker on exit.
+ if (mark != null)
+ push_source(new FixedTokenSource(Arrays.asList(mark)), true);
+ }
+ }
+
protected void pragma(@Nonnull Token name, @Nonnull List<Token> value)
throws IOException,
LexerException {
+ if (getFeature(Feature.PRAGMA_ONCE)) {
+ if ("once".equals(name.getText())) {
+ pragma_once(name);
+ return;
+ }
+ }
warning(name, "Unknown #" + "pragma: " + name.getText());
}
diff --git a/src/test/java/org/anarres/cpp/CppReaderTest.java b/src/test/java/org/anarres/cpp/CppReaderTest.java
index 27eba06..6f98bd4 100644
--- a/src/test/java/org/anarres/cpp/CppReaderTest.java
+++ b/src/test/java/org/anarres/cpp/CppReaderTest.java
@@ -4,31 +4,52 @@ import java.util.Collections;
import java.io.StringReader;
import java.io.BufferedReader;
+import javax.annotation.Nonnull;
import org.junit.Test;
+import static org.junit.Assert.*;
public class CppReaderTest {
- private void testCppReader(String in, String out)
+ public static String testCppReader(@Nonnull String in, Feature... f)
throws Exception {
- System.out.println("Testing " + in + " => " + out);
+ System.out.println("Testing " + in);
StringReader r = new StringReader(in);
CppReader p = new CppReader(r);
p.getPreprocessor().setSystemIncludePath(
Collections.singletonList("src/test/resources")
);
- p.getPreprocessor().getFeatures().add(Feature.LINEMARKERS);
+ p.getPreprocessor().addFeatures(f);
BufferedReader b = new BufferedReader(p);
+ StringBuilder out = new StringBuilder();
String line;
while ((line = b.readLine()) != null) {
System.out.println(" >> " + line);
+ out.append(line).append("\n");
}
+
+ return out.toString();
}
@Test
public void testCppReader()
throws Exception {
- testCppReader("#include <test0.h>\n", "ab");
+ testCppReader("#include <test0.h>\n", Feature.LINEMARKERS);
+ }
+
+ @Test
+ public void testPragmaOnce()
+ throws Exception {
+ // The newlines are irrelevant, We want exactly one "foo"
+ String out = testCppReader("#include <once.c>\n", Feature.PRAGMA_ONCE);
+ assertEquals("foo", out.trim());
+ }
+
+ @Test
+ public void testPragmaOnceWithMarkers()
+ throws Exception {
+ // The newlines are irrelevant, We want exactly one "foo"
+ testCppReader("#include <once.c>\n", Feature.PRAGMA_ONCE, Feature.LINEMARKERS);
}
}
diff --git a/src/test/resources/once.c b/src/test/resources/once.c
new file mode 100644
index 0000000..fd50d46
--- /dev/null
+++ b/src/test/resources/once.c
@@ -0,0 +1,2 @@
+#include "once.h"
+#include "once.h"
diff --git a/src/test/resources/once.h b/src/test/resources/once.h
new file mode 100644
index 0000000..b6a482e
--- /dev/null
+++ b/src/test/resources/once.h
@@ -0,0 +1,2 @@
+#pragma once
+foo