From 8880128db2660d6a9494911fbd14bcc5aaa39c60 Mon Sep 17 00:00:00 2001 From: Shevek Date: Wed, 10 Sep 2014 23:02:35 -0700 Subject: Modernize: Use slf4j and joptsimple. --- build.gradle | 11 +- install | 1 + src/main/java/org/anarres/cpp/CppReader.java | 17 +- .../anarres/cpp/DefaultPreprocessorListener.java | 12 +- src/main/java/org/anarres/cpp/Feature.java | 1 - src/main/java/org/anarres/cpp/Main.java | 333 +++++++-------------- src/main/java/org/anarres/cpp/Preprocessor.java | 18 +- .../java/org/anarres/cpp/PreprocessorListener.java | 12 +- src/main/java/org/anarres/cpp/Warning.java | 2 +- 9 files changed, 149 insertions(+), 258 deletions(-) create mode 120000 install diff --git a/build.gradle b/build.gradle index ce1a8cd..3460d83 100644 --- a/build.gradle +++ b/build.gradle @@ -29,11 +29,16 @@ apply plugin: 'application' apply plugin: 'velocity' dependencies { + def slf4jVersion = '1.7.7' + compile 'com.google.code.findbugs:jsr305:2.0.2' - compile 'gnu.getopt:java-getopt:1.0.13' - // compile 'net.sf.jopt-simple:jopt-simple:4.7' + // compile 'gnu.getopt:java-getopt:1.0.13' + compile "org.slf4j:slf4j-api:$slf4jVersion" + compile 'net.sf.jopt-simple:jopt-simple:4.7' compile 'org.apache.ant:ant:1.7.0' + runtime "org.slf4j:slf4j-jcl:$slf4jVersion" + testCompile 'junit:junit:4.11' // testCompile 'com.google.guava:guava:18.0' testCompile 'commons-logging:commons-logging:1.2' @@ -47,6 +52,8 @@ velocity { context m } +mainClassName = "org.anarres.cpp.Main" + test { systemProperty 'org.apache.commons.logging.Log', 'org.apache.commons.logging.impl.SimpleLog' systemProperty 'org.apache.commons.logging.simplelog.defaultlog', 'debug' diff --git a/install b/install new file mode 120000 index 0000000..a40f9f7 --- /dev/null +++ b/install @@ -0,0 +1 @@ +build/install/jcpp/ \ No newline at end of file diff --git a/src/main/java/org/anarres/cpp/CppReader.java b/src/main/java/org/anarres/cpp/CppReader.java index 9157a93..5517f47 100644 --- a/src/main/java/org/anarres/cpp/CppReader.java +++ b/src/main/java/org/anarres/cpp/CppReader.java @@ -19,6 +19,7 @@ package org.anarres.cpp; import java.io.Closeable; import java.io.IOException; import java.io.Reader; +import javax.annotation.Nonnull; import static org.anarres.cpp.Token.CCOMMENT; import static org.anarres.cpp.Token.CPPCOMMENT; import static org.anarres.cpp.Token.EOF; @@ -34,11 +35,11 @@ import static org.anarres.cpp.Token.EOF; */ public class CppReader extends Reader implements Closeable { - private Preprocessor cpp; + private final Preprocessor cpp; private String token; private int idx; - public CppReader(final Reader r) { + public CppReader(@Nonnull final Reader r) { cpp = new Preprocessor(new LexerSource(r, true) { @Override public String getName() { @@ -50,7 +51,7 @@ public class CppReader extends Reader implements Closeable { idx = 0; } - public CppReader(Preprocessor p) { + public CppReader(@Nonnull Preprocessor p) { cpp = p; token = ""; idx = 0; @@ -59,6 +60,7 @@ public class CppReader extends Reader implements Closeable { /** * Returns the Preprocessor used by this CppReader. */ + @Nonnull public Preprocessor getPreprocessor() { return cpp; } @@ -68,7 +70,7 @@ public class CppReader extends Reader implements Closeable { * * This is a convnience method. */ - public void addMacro(String name) + public void addMacro(@Nonnull String name) throws LexerException { cpp.addMacro(name); } @@ -78,7 +80,7 @@ public class CppReader extends Reader implements Closeable { * * This is a convnience method. */ - public void addMacro(String name, String value) + public void addMacro(@Nonnull String name, @Nonnull String value) throws LexerException { cpp.addMacro(name, value); } @@ -138,10 +140,7 @@ public class CppReader extends Reader implements Closeable { @Override public void close() throws IOException { - if (cpp != null) { - cpp.close(); - cpp = null; - } + cpp.close(); token = null; } diff --git a/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java b/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java index 6d3929d..68f6e89 100644 --- a/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java +++ b/src/main/java/org/anarres/cpp/DefaultPreprocessorListener.java @@ -17,6 +17,9 @@ package org.anarres.cpp; * permissions and limitations under the License. */ import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A handler for preprocessor events, primarily errors and warnings. @@ -27,6 +30,8 @@ import javax.annotation.Nonnegative; */ public class DefaultPreprocessorListener implements PreprocessorListener { + private static final Logger LOG = LoggerFactory.getLogger(DefaultPreprocessorListener.class); + private int errors; private int warnings; @@ -49,8 +54,8 @@ public class DefaultPreprocessorListener implements PreprocessorListener { return warnings; } - protected void print(String msg) { - System.err.println(msg); + protected void print(@Nonnull String msg) { + LOG.info(msg); } /** @@ -60,6 +65,7 @@ public class DefaultPreprocessorListener implements PreprocessorListener { * implementation. It may simply record the error message, or * it may throw an exception. */ + @Override public void handleWarning(Source source, int line, int column, String msg) throws LexerException { @@ -75,6 +81,7 @@ public class DefaultPreprocessorListener implements PreprocessorListener { * implementation. It may simply record the error message, or * it may throw an exception. */ + @Override public void handleError(Source source, int line, int column, String msg) throws LexerException { @@ -83,6 +90,7 @@ public class DefaultPreprocessorListener implements PreprocessorListener { + ": error: " + msg); } + @Override public void handleSourceChange(Source source, String event) { } diff --git a/src/main/java/org/anarres/cpp/Feature.java b/src/main/java/org/anarres/cpp/Feature.java index cc7f818..369d79c 100644 --- a/src/main/java/org/anarres/cpp/Feature.java +++ b/src/main/java/org/anarres/cpp/Feature.java @@ -33,7 +33,6 @@ public enum Feature { KEEPCOMMENTS, /** Preserves comments in the lexed output, even when inactive. */ KEEPALLCOMMENTS, - VERBOSE, DEBUG, /** Supports lexing of objective-C. */ OBJCSYNTAX, diff --git a/src/main/java/org/anarres/cpp/Main.java b/src/main/java/org/anarres/cpp/Main.java index 3bc02eb..5a3e113 100644 --- a/src/main/java/org/anarres/cpp/Main.java +++ b/src/main/java/org/anarres/cpp/Main.java @@ -16,54 +16,27 @@ */ package org.anarres.cpp; -import gnu.getopt.Getopt; -import gnu.getopt.LongOpt; import java.io.File; import java.io.PrintStream; +import java.util.Arrays; import java.util.EnumSet; +import java.util.List; +import javax.annotation.Nonnull; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import joptsimple.ValueConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * (Currently a simple test class). */ public class Main { - private static class Option extends LongOpt { - - private final String eg; - private final 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 final Logger LOG = LoggerFactory.getLogger(Main.class); + @Nonnull private static CharSequence getWarnings() { StringBuilder buf = new StringBuilder(); for (Warning w : Warning.values()) { @@ -80,12 +53,52 @@ public class Main { } 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; + + OptionParser parser = new OptionParser(); + OptionSpec helpOption = parser.accepts("help", + "Displays command-line help.") + .forHelp(); + OptionSpec versionOption = parser.acceptsAll(Arrays.asList("version"), + "Displays the product version (" + Version.getVersion() + ") and exits.") + .forHelp(); + + OptionSpec debugOption = parser.acceptsAll(Arrays.asList("debug"), + "Enables debug output."); + + OptionSpec defineOption = parser.acceptsAll(Arrays.asList("define", "D"), + "Defines the given macro.") + .withRequiredArg().ofType(String.class).describedAs("name[=definition]"); + OptionSpec undefineOption = parser.acceptsAll(Arrays.asList("undefine", "U"), + "Undefines the given macro, previously either builtin or defined using -D.") + .withRequiredArg().describedAs("name"); + OptionSpec includeOption = parser.accepts("include", + "Process file as if \"#" + "include \"file\"\" appeared as the first line of the primary source file.") + .withRequiredArg().ofType(File.class).describedAs("file"); + OptionSpec incdirOption = parser.acceptsAll(Arrays.asList("incdir", "I"), + "Adds the directory dir to the list of directories to be searched for header files.") + .withRequiredArg().ofType(File.class).describedAs("dir"); + OptionSpec iquoteOption = parser.acceptsAll(Arrays.asList("iquote"), + "Adds the directory dir to the list of directories to be searched for header files included using \"\".") + .withRequiredArg().ofType(File.class).describedAs("dir"); + OptionSpec warningOption = parser.acceptsAll(Arrays.asList("warning", "W"), + "Enables the named warning class (" + getWarnings() + ").") + .withRequiredArg().ofType(String.class).describedAs("warning"); + OptionSpec noWarningOption = parser.acceptsAll(Arrays.asList("no-warnings", "w"), + "Disables ALL warnings."); + OptionSpec inputsOption = parser.nonOptions() + .ofType(File.class).describedAs("Files to process."); + + OptionSet options = parser.parse(args); + + if (options.has(helpOption)) { + parser.printHelpOn(System.out); + return; + } + + if (options.has(versionOption)) { + version(System.out); + return; + } Preprocessor pp = new Preprocessor(); pp.addFeature(Feature.DIGRAPHS); @@ -100,78 +113,56 @@ public class Main { 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. */ + if (options.has(debugOption)) + pp.addFeature(Feature.DEBUG); - } + if (options.has(noWarningOption)) + pp.getWarnings().clear(); + + for (String warning : options.valuesOf(warningOption)) { + warning = warning.toUpperCase(); + warning = warning.replace('-', '_'); + if (warning.equals("ALL")) + pp.addWarnings(EnumSet.allOf(Warning.class)); + else + pp.addWarning(Enum.valueOf(Warning.class, warning)); + } + + for (String arg : options.valuesOf(defineOption)) { + int idx = arg.indexOf('='); + if (idx == -1) + pp.addMacro(arg); + else + pp.addMacro(arg.substring(0, idx), arg.substring(idx + 1)); } + for (String arg : options.valuesOf(undefineOption)) { + pp.getMacros().remove(arg); + } + + for (File dir : options.valuesOf(incdirOption)) + pp.getSystemIncludePath().add(dir.getAbsolutePath()); + for (File dir : options.valuesOf(iquoteOption)) + pp.getQuoteIncludePath().add(dir.getAbsolutePath()); + for (File file : options.valuesOf(includeOption)) + // Comply exactly with spec. + pp.addInput(new StringLexerSource("#" + "include \"" + file + "\"\n")); - for (int i = g.getOptind(); i < args.length; i++) - pp.addInput(new FileLexerSource(new File(args[i]))); - if (g.getOptind() == args.length) + List inputs = options.valuesOf(inputsOption); + if (inputs.isEmpty()) { pp.addInput(new InputLexerSource(System.in)); + } else { + for (File input : inputs) + pp.addInput(new FileLexerSource(input)); + } - if (pp.getFeature(Feature.VERBOSE)) { - System.err.println("#" + "include \"...\" search starts here:"); + if (pp.getFeature(Feature.DEBUG)) { + LOG.info("#" + "include \"...\" search starts here:"); for (String dir : pp.getQuoteIncludePath()) - System.err.println(" " + dir); - System.err.println("#" + "include <...> search starts here:"); + LOG.info(" " + dir); + LOG.info("#" + "include <...> search starts here:"); for (String dir : pp.getSystemIncludePath()) - System.err.println(" " + dir); - System.err.println("End of search list."); + LOG.info(" " + dir); + LOG.info("End of search list."); } try { @@ -184,137 +175,21 @@ public class Main { System.out.print(tok.getText()); } } catch (Exception e) { - e.printStackTrace(System.err); + StringBuilder buf = new StringBuilder("Preprocessor failed:\n"); Source s = pp.getSource(); while (s != null) { - System.err.println(" -> " + s); + buf.append(" -> ").append(s).append("\n"); s = s.getParent(); } + LOG.error(buf.toString(), e); } } - private void version(PrintStream out) { + private static void version(@Nonnull 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 (Option opt : opts) { - char c = (char) opt.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 (opt.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: - *
-     * prefix     in[0]
-     * <--indent-> in[1]
-     * <--indent-> in[2]
-     * <-----width---->
-     * 
- */ - /* 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 (Option option : options) { - StringBuilder line = new StringBuilder(); - Option opt = option; - 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/Preprocessor.java b/src/main/java/org/anarres/cpp/Preprocessor.java index 43fba89..6bcf3fb 100644 --- a/src/main/java/org/anarres/cpp/Preprocessor.java +++ b/src/main/java/org/anarres/cpp/Preprocessor.java @@ -33,6 +33,8 @@ import java.util.Stack; import java.util.TreeMap; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static org.anarres.cpp.PreprocessorCommand.*; import static org.anarres.cpp.Token.*; @@ -73,6 +75,8 @@ import static org.anarres.cpp.Token.*; */ public class Preprocessor implements Closeable { + private static final Logger LOG = LoggerFactory.getLogger(Preprocessor.class); + private static final Source INTERNAL = new Source() { @Override public Token token() @@ -600,7 +604,7 @@ public class Preprocessor implements Closeable { Token tok = source_token; source_token = null; if (getFeature(Feature.DEBUG)) - System.err.println("Returning unget token " + tok); + LOG.debug("Returning unget token " + tok); return tok; } @@ -622,7 +626,7 @@ public class Preprocessor implements Closeable { continue; } if (getFeature(Feature.DEBUG)) - System.err.println("Returning fresh token " + tok); + LOG.debug("Returning fresh token " + tok); return tok; } } @@ -1064,7 +1068,7 @@ public class Preprocessor implements Closeable { } if (getFeature(Feature.DEBUG)) - System.err.println("Defined macro " + m); + LOG.debug("Defined macro " + m); addMacro(m); return tok; /* NL or EOF. */ @@ -1104,7 +1108,7 @@ public class Preprocessor implements Closeable { if (!file.isFile()) return false; if (getFeature(Feature.DEBUG)) - System.err.println("pp: including " + file); + LOG.debug("pp: including " + file); includes.add(file); push_source(file.getSource(), true); return true; @@ -1497,9 +1501,7 @@ public class Preprocessor implements Closeable { throws IOException, LexerException { /* - * System.out.flush(); * (new Exception("expr(" + priority + ") called")).printStackTrace(); - * System.err.flush(); */ Token tok = expr_token(); @@ -1638,9 +1640,7 @@ public class Preprocessor implements Closeable { } /* - * System.out.flush(); * (new Exception("expr returning " + lhs)).printStackTrace(); - * System.err.flush(); */ // System.out.println("expr returning " + lhs); return lhs; @@ -2059,7 +2059,7 @@ public class Preprocessor implements Closeable { LexerException { Token tok = _token(); if (getFeature(Feature.DEBUG)) - System.err.println("pp: Returning " + tok); + LOG.debug("pp: Returning " + tok); return tok; } diff --git a/src/main/java/org/anarres/cpp/PreprocessorListener.java b/src/main/java/org/anarres/cpp/PreprocessorListener.java index 6a4cb22..3443714 100644 --- a/src/main/java/org/anarres/cpp/PreprocessorListener.java +++ b/src/main/java/org/anarres/cpp/PreprocessorListener.java @@ -16,6 +16,8 @@ */ package org.anarres.cpp; +import javax.annotation.Nonnull; + /** * A handler for preprocessor events, primarily errors and warnings. * @@ -32,8 +34,8 @@ public interface PreprocessorListener { * 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) + public void handleWarning(@Nonnull Source source, int line, int column, + @Nonnull String msg) throws LexerException; /** @@ -43,10 +45,10 @@ public interface PreprocessorListener { * 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) + public void handleError(@Nonnull Source source, int line, int column, + @Nonnull String msg) throws LexerException; - public void handleSourceChange(Source source, String event); + public void handleSourceChange(@Nonnull Source source, @Nonnull String event); } diff --git a/src/main/java/org/anarres/cpp/Warning.java b/src/main/java/org/anarres/cpp/Warning.java index da2b2c6..80d184a 100644 --- a/src/main/java/org/anarres/cpp/Warning.java +++ b/src/main/java/org/anarres/cpp/Warning.java @@ -28,5 +28,5 @@ public enum Warning { UNUSED_MACROS, ENDIF_LABELS, ERROR, - // SYSTEM_HEADERS + // SYSTEM_HEADERS } -- cgit v1.2.3