From efc10c0f0bc0e8c245282fc55d28adb2ce1967eb Mon Sep 17 00:00:00 2001 From: Shevek Date: Sat, 28 Dec 2013 04:56:55 -0800 Subject: Fix #pragma once. --- src/main/java/org/anarres/cpp/Feature.java | 5 +- src/main/java/org/anarres/cpp/FileLexerSource.java | 94 ++++++++++++---------- src/main/java/org/anarres/cpp/Preprocessor.java | 90 ++++++++++++++------- src/test/java/org/anarres/cpp/CppReaderTest.java | 29 ++++++- src/test/resources/once.c | 2 + src/test/resources/once.h | 2 + 6 files changed, 145 insertions(+), 77 deletions(-) create mode 100644 src/test/resources/once.c create mode 100644 src/test/resources/once.h (limited to 'src') 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 inputs; + private final List inputs; /* The fundamental engine. */ - private Map macros; - private Stack states; + private final Map macros; + private final Stack states; private Source source; /* Miscellaneous support. */ private int counter; + private final Set onceseenpaths = new HashSet(); /* Support junk to make it work like cpp */ private List quoteincludepath; /* -iquote */ @@ -112,8 +114,8 @@ public class Preprocessor implements Closeable { private List sysincludepath; /* -I */ private List frameworkspath; - private Set features; - private Set warnings; + private final Set features; + private final Set warnings; private VirtualFileSystem filesystem; private PreprocessorListener listener; @@ -217,6 +219,13 @@ public class Preprocessor implements Closeable { features.addAll(f); } + /** + * 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 expand(List arg) + @Nonnull + /* pp */ List expand(@Nonnull List arg) throws IOException, LexerException { List expansion = new ArrayList(); @@ -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 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 \n", "ab"); + testCppReader("#include \n", Feature.LINEMARKERS); + } + + @Test + public void testPragmaOnce() + throws Exception { + // The newlines are irrelevant, We want exactly one "foo" + String out = testCppReader("#include \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 \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 -- cgit v1.2.3