diff --git a/.gitignore b/.gitignore index 87b4cdd3d7c6a41502ca98703abeeb69a1d536fb..6dbaade2d929c7a425eca520d34b28c2a3a30369 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ build src/gen-res/ src/gen/ -out/ +out*/ *.class diff --git a/src/main/java/org/jastadd/JastAddConfiguration.java b/src/main/java/org/jastadd/JastAddConfiguration.java index 0620a16ee7862dc9da15af4b8c16b5babec7b94b..30774005356c0eb30354b7fb76e1bafe67d00873 100644 --- a/src/main/java/org/jastadd/JastAddConfiguration.java +++ b/src/main/java/org/jastadd/JastAddConfiguration.java @@ -31,8 +31,8 @@ import org.jastadd.option.ArgumentParser; import org.jastadd.option.Option; import java.io.PrintStream; -import java.util.Collection; -import java.util.LinkedList; +import java.util.*; +import java.util.stream.Collectors; /** * Tracks JastAdd configuration options. @@ -46,22 +46,52 @@ public class JastAddConfiguration extends org.jastadd.Configuration { */ final boolean unknownOptions; + private boolean isJastAddCompliant; + + public boolean isJastAddCompliant() { + return isJastAddCompliant; + } + /** * Parse options from an argument list. * * @param args Command-line arguments to build configuration from * @param err output stream to print configuration warnings to */ - public JastAddConfiguration(String[] args, PrintStream err, Collection<Option<?>> extraOptions) { + public JastAddConfiguration(String[] args, PrintStream err, boolean isJastAddCompliant, Collection<Option<?>> extraOptions) { ArgumentParser argParser = new ArgumentParser(); - argParser.addOptions(allJastAddOptions()); - argParser.addOptions(extraOptions); + this.isJastAddCompliant = isJastAddCompliant; + if (isJastAddCompliant) { + Collection<Option<?>> jastAddOptions = allJastAddOptions(); + argParser.addOptions(jastAddOptions); + + // if the JastAdd options are supported, we have to check for duplicates! + Set<String> jastAddOptionNames = jastAddOptions.stream().map(o -> o.name()).collect(Collectors.toSet()); + for (Option option : extraOptions) { + if (jastAddOptionNames.contains(option.name())) { + System.err.println("Unable to add option '" + option.name() + "', because there is a JastAdd option with the same name."); + } else { + argParser.addOption(option); + } + } + } else { + argParser.addOptions(extraOptions); + } + unknownOptions = !argParser.parseArgs(args, err); filenames = argParser.getFilenames(); } + /** + * @return all files + */ + @Override + public Collection<String> getFiles() { + return Collections.unmodifiableCollection(filenames); + } + private Collection<Option<?>> allJastAddOptions() { - Collection<Option<?>> allOptions = new LinkedList<Option<?>>(); + Collection<Option<?>> allOptions = new LinkedList<>(); allOptions.add(ASTNodeOption); allOptions.add(ListOption); allOptions.add(OptOption); diff --git a/src/main/java/org/jastadd/relast/compiler/AbstractCompiler.java b/src/main/java/org/jastadd/relast/compiler/AbstractCompiler.java index cb4a8399b790fd2b9d44321def104c46f6d20246..1330ebe284b62a90480959eb35eb23dbda831540 100644 --- a/src/main/java/org/jastadd/relast/compiler/AbstractCompiler.java +++ b/src/main/java/org/jastadd/relast/compiler/AbstractCompiler.java @@ -31,13 +31,8 @@ public abstract class AbstractCompiler { options = new ArrayList<>(); initOptions(); - if (jastAddCompliant) { - configuration = new JastAddConfiguration(args, System.err, options); - } else { - commandLine = new ArgumentParser(); - commandLine.addOptions(options); - commandLine.parseArgs(args, System.err); - } + configuration = new JastAddConfiguration(args, System.err, jastAddCompliant, options); + return compile(); } diff --git a/src/main/java/org/jastadd/relast/compiler/RelastSourceToSourceCompiler.java b/src/main/java/org/jastadd/relast/compiler/RelastSourceToSourceCompiler.java index e8f86a0079ea10b4f177a61342a8d7e9ac6c7593..7d6949f5ff7ec74d2e1963e564bff9d633d4fbec 100644 --- a/src/main/java/org/jastadd/relast/compiler/RelastSourceToSourceCompiler.java +++ b/src/main/java/org/jastadd/relast/compiler/RelastSourceToSourceCompiler.java @@ -10,16 +10,18 @@ import org.jastadd.relast.scanner.RelAstScanner; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; -import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; public class RelastSourceToSourceCompiler extends AbstractCompiler { - protected ValueOption optionOutputDir; - protected ValueOption optionInputDir; + protected ValueOption optionOutputBaseDir; + protected ValueOption optionInputBaseDir; public RelastSourceToSourceCompiler(String name, boolean jastAddCompliant) { super(name, jastAddCompliant); @@ -34,96 +36,109 @@ public class RelastSourceToSourceCompiler extends AbstractCompiler { } } + protected static boolean isGrammarFile(String fileName) { + String extension = fileName.subSequence(fileName.lastIndexOf("."), fileName.length()).toString(); + return extension.equals(".relast") || extension.equals(".ast"); + } + @Override protected void initOptions() { - optionOutputDir = addOption(new ValueOption("outputDir", "target directory for the generated files.")); - optionInputDir = addOption(new ValueOption("inputDir", "input directory.")); + optionOutputBaseDir = addOption(new ValueOption("outputBaseDir", "base directory for generated files")); + optionInputBaseDir = addOption(new ValueOption("inputBaseDir", "base directory for input files")); } @Override protected int compile() throws CompilerException { - Path inputPath; - if (optionInputDir.isMatched()) { - inputPath = Paths.get(optionInputDir.value()); + final Path inputBasePath; + if (optionInputBaseDir.isMatched()) { + inputBasePath = Paths.get(optionInputBaseDir.value()).toAbsolutePath(); } else { - inputPath = Paths.get("."); - printMessage("No input dir is set. Assuming current directory '" + inputPath.toAbsolutePath().toString() + "'."); + inputBasePath = Paths.get(".").toAbsolutePath(); + printMessage("No input base dir is set. Assuming current directory '" + inputBasePath.toAbsolutePath().toString() + "'."); } - if (!inputPath.toFile().exists()) { - printMessage("Input path '" + inputPath.toAbsolutePath().toString() + "' does not exist. Exiting..."); + if (!inputBasePath.toFile().exists()) { + printMessage("Input path '" + inputBasePath.toAbsolutePath().toString() + "' does not exist. Exiting..."); System.exit(-1); - } else if (!inputPath.toFile().isDirectory()) { - printMessage("Input path '" + inputPath.toAbsolutePath().toString() + "' is not a directory. Exiting..."); + } else if (!inputBasePath.toFile().isDirectory()) { + printMessage("Input path '" + inputBasePath.toAbsolutePath().toString() + "' is not a directory. Exiting..."); System.exit(-1); } - Path outputPath; - if (optionOutputDir.isMatched()) { - outputPath = Paths.get(optionOutputDir.value()); + final Path outputBasePath; + if (optionOutputBaseDir.isMatched()) { + outputBasePath = Paths.get(optionOutputBaseDir.value()).toAbsolutePath(); } else { - outputPath = Paths.get("./gen/"); - printMessage("No output dir is set. Assuming '" + outputPath.toAbsolutePath().toString() + "'."); + throw new CompilerException("No output base dir is set."); } - if (outputPath.toFile().exists() && !outputPath.toFile().isDirectory()) { - printMessage("Output path '" + inputPath.toAbsolutePath().toString() + "' exists, but is not a directory. Exiting..."); + if (outputBasePath.toFile().exists() && !outputBasePath.toFile().isDirectory()) { + printMessage("Output path '" + inputBasePath.toAbsolutePath().toString() + "' exists, but is not a directory. Exiting..."); } printMessage("Running RelAST Preprocessor"); - Program program = parseProgram(inputPath); + // gather all files + Collection<Path> inputFiles = new ArrayList<>(); + getConfiguration().getFiles().forEach(name -> relativizeFileName(inputBasePath, Paths.get(name)).ifPresent(path -> inputFiles.add(path))); + + + Program program = parseProgram(inputFiles); printMessage("Writing output files"); for (GrammarFile grammarFile : program.getGrammarFileList()) { // TODO decide and document what the file name should be, the full path or a simple name? - writeToFile(outputPath + grammarFile.getFileName() + "/Grammar.relast", grammarFile.generateAbstractGrammar()); + writeToFile(outputBasePath.resolve(inputBasePath.relativize(Paths.get(grammarFile.getFileName()))), grammarFile.generateAbstractGrammar()); } - - writeToFile(outputPath + "/Grammar.relast", program.generateAbstractGrammar()); return 0; } + private Optional<Path> relativizeFileName(Path inputBasePath, Path filePath) { + if (filePath.isAbsolute()) { + if (filePath.startsWith(inputBasePath)) { + return Optional.of(filePath.relativize(inputBasePath)); + } else { + printMessage("Path '" + filePath + "' is not contained in the base path '" + inputBasePath + "'."); + return Optional.empty(); + } + } else { + return Optional.of(inputBasePath.resolve(filePath)); + } + } + private void printMessage(String message) { System.out.println(message); } - private void writeToFile(String filename, String str) throws CompilerException { - try { - PrintWriter writer = new PrintWriter(filename); + private void writeToFile(Path path, String str) throws CompilerException { + try (PrintWriter writer = new PrintWriter(path.toFile())) { writer.print(str); - writer.close(); } catch (Exception e) { - throw new CompilerException("Could not write to file " + filename, e); + throw new CompilerException("Could not write to file " + path, e); } } - private Program parseProgram(Path inputPath) throws CompilerException { + private Program parseProgram(Collection<Path> inputFiles) throws CompilerException { Program program = new Program(); - try (DirectoryStream<Path> stream = Files.newDirectoryStream(inputPath, "*.relast")) { - RelAstParser parser = new RelAstParser(); - stream.forEach(path -> { - try (BufferedReader reader = Files.newBufferedReader(path)) { - RelAstScanner scanner = new RelAstScanner(reader); - GrammarFile inputGrammar = (GrammarFile) parser.parse(scanner); - inputGrammar.dumpTree(System.out); - program.addGrammarFile(inputGrammar); - inputGrammar.treeResolveAll(); - } catch (IOException | Parser.Exception e) { - printMessage("Could not parse grammar file " + path); - e.printStackTrace(); + RelAstParser parser = new RelAstParser(); + inputFiles.stream().filter(path -> isGrammarFile(path.toString())).forEach( + path -> { + try (BufferedReader reader = Files.newBufferedReader(path)) { + RelAstScanner scanner = new RelAstScanner(reader); + GrammarFile inputGrammar = (GrammarFile) parser.parse(scanner); + inputGrammar.setFileName(path.toString()); + program.addGrammarFile(inputGrammar); + inputGrammar.treeResolveAll(); + } catch (IOException | Parser.Exception e) { + printMessage("Could not parse grammar file " + path); + e.printStackTrace(); + } } - }); - } catch (IOException e) { - printMessage("Unable to iterate over input path '" + inputPath.toAbsolutePath().toString() + "'. Exiting..."); - e.printStackTrace(); - System.exit(-1); - } - + ); return program; - } +} } diff --git a/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java b/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java index fdc383d8efc181dfb27e17a7bc41a3c64c19bf58..c87e51f7dbc70644b621998548d4f942160e0ea9 100644 --- a/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java +++ b/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java @@ -29,8 +29,9 @@ public class RelAstTest { } String[] args = { - "--outputDir=" + outputDir, - "--inputDir=" + inputDir + "--outputBaseDir=" + outputDir, + "--inputBaseDir=" + inputDir, + "Example.relast" }; new RelastSourceToSourceCompiler("testCompiler", jastAddCompliant).run(args); @@ -38,7 +39,7 @@ public class RelAstTest { @Test void transformMinimalExample() throws CompilerException { - transform(false,"src/test/resources/in", "src/test/resources/out"); - transform(true,"src/test/resources/in", "src/test/resources/out"); + transform(false,"src/test/resources/in", "src/test/resources/out-simple"); + transform(true,"src/test/resources/in", "src/test/resources/out-compliant"); } }