diff options
author | Francis Gagné <fragag1@gmail.com> | 2014-09-13 21:49:30 -0400 |
---|---|---|
committer | Francis Gagné <fragag1@gmail.com> | 2014-09-13 21:59:04 -0400 |
commit | ed2f84864a7e841249e183a44178fa2c1b38de3b (patch) | |
tree | 073577d5e4319a5fba38204e8e8743eb702c0b5d | |
parent | 72bfd73989cd2d73863fc31146c7b173febfe879 (diff) |
Initial version
-rw-r--r-- | pom.xml | 14 | ||||
-rw-r--r-- | src/main/java/org/reasm/batch/Assembler.java | 187 | ||||
-rw-r--r-- | src/main/java/org/reasm/batch/LocalFileFetcher.java | 57 |
3 files changed, 258 insertions, 0 deletions
@@ -26,4 +26,18 @@ </plugin> </plugins> </build> + + <dependencies> + <dependency> + <groupId>org.reasm</groupId> + <artifactId>reasm-core</artifactId> + <version>0.0.1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.reasm</groupId> + <artifactId>reasm-m68k</artifactId> + <version>0.0.1-SNAPSHOT</version> + <optional>true</optional> + </dependency> + </dependencies> </project> diff --git a/src/main/java/org/reasm/batch/Assembler.java b/src/main/java/org/reasm/batch/Assembler.java new file mode 100644 index 0000000..eec3dcb --- /dev/null +++ b/src/main/java/org/reasm/batch/Assembler.java @@ -0,0 +1,187 @@ +package org.reasm.batch; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.ServiceLoader; + +import org.reasm.*; +import org.reasm.messages.InternalAssemblerErrorMessage; +import org.reasm.source.SourceFile; + +/** + * The main class of the batch assembler. + * + * @author Francis Gagné + */ +public class Assembler { + + // TODO + // - Read all the Configuration data from a file, not from command-line arguments + // - Command-line arguments may influence the assembler's behavior (e.g. emit a listing or not) + + /** + * The entry point of the batch assembler. + * + * @param args + * the command-line arguments + */ + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Usage: java -jar reasm-batch.jar <file> <initialArchitecture>"); + System.exit(1); + } else { + try { + // Initialize the environment. + final ArrayList<Architecture> architectures = new ArrayList<>(); + final ServiceLoader<ArchitectureProvider> loader = ServiceLoader.load(ArchitectureProvider.class); + for (ArchitectureProvider architectureProvider : loader) { + for (Architecture architecture : architectureProvider) { + architectures.add(architecture); + } + } + + final Environment environment = Environment.DEFAULT.addArchitectures(architectures); + + // Set the initial architecture. + final Architecture initialArchitecture = environment.findArchitectureByName(args[1]); + if (initialArchitecture == null) { + System.err.println("Architecture \"" + args[1] + "\" was not found"); + return; + } + + // Create the main source file. + final Path path = Paths.get(args[0]); + final SourceFile mainSourceFile = new SourceFile(new String(Files.readAllBytes(path), "UTF-8"), path.getFileName() + .toString()); + + // Create a configuration. + final FileFetcher fileFetcher = new LocalFileFetcher(args[0]); + final Configuration configuration = new Configuration(environment, mainSourceFile, initialArchitecture) + .setFileFetcher(fileFetcher); + + // Assemble the source based on this configuration. + final Assembly assembly = new Assembly(configuration); + + // Assemble the file. + while (performPass(assembly)) { + } + + if (assembly.getGravity() != MessageGravity.NONE) { + displayMessages(assembly); + } else { + System.out.println("No assembly messages."); + } + + if (assembly.getGravity().compareTo(MessageGravity.ERROR) >= 0) { + System.exit(1); + } + + outputFile(assembly, "a.out"); + } catch (FileNotFoundException e) { + System.err.println(args[0] + ": " + e.toString()); + System.exit(1); + } catch (UnsupportedEncodingException e) { + System.err.println("The UTF-8 encoding is not supported by this Java runtime."); + System.exit(1); + } catch (IOException e) { + System.err.println(args[0] + ": " + e.toString()); + System.exit(1); + } + } + } + + /** + * Displays the messages that occurred in the assembly. + * + * @param assembly + * the assembly + */ + private static void displayMessages(Assembly assembly) { + for (AssemblyMessage message : assembly.getMessages()) { + final AssemblyStep step = message.getStep(); + + if (step == null) { + System.out.printf("(no location): %s: %s%n", getGravityName(message.getGravity()), message.getText()); + } else { + AssemblyStepLocation location = step.getLocation(); + System.out.printf("%s: %s: %s%n> %s%n", location.getFullPath(), getGravityName(message.getGravity()), + message.getText(), location.getSourceLocation().getTextReader().readToString()); + } + + if (message instanceof InternalAssemblerErrorMessage) { + ((InternalAssemblerErrorMessage) message).getThrowable().printStackTrace(); + } + } + } + + /** + * Gets the description of a message gravity level. + * + * @param gravity + * the message gravity level + * @return the description + */ + private static String getGravityName(MessageGravity gravity) { + switch (gravity) { + case NONE: + return "Message"; + + case INFORMATION: + return "Information"; + + case WARNING: + return "Warning"; + + case ERROR: + return "Error"; + + case FATAL_ERROR: + return "Fatal error"; + + default: + return "Unknown"; + } + } + + /** + * Outputs the assembly to a file. + * + * @param assembly + * the assembly + * @param fileName + * the name of the output file + * @throws IOException + * an I/O exception occurred while working with the output file + * @throws FileNotFoundException + * the output file could not be created or opened for writing + */ + private static void outputFile(Assembly assembly, String fileName) throws IOException { + try (FileOutputStream out = new FileOutputStream(fileName)) { + assembly.writeAssembledDataTo(out); + } + } + + /** + * Performs a pass in the specified assembly. + * + * @param assembly + * the assembly in which to perform a pass + * @return <code>true</code> if there are more passes to perform, or <code>false</code> if the assembly is complete. + */ + private static boolean performPass(Assembly assembly) { + System.out.printf("Pass %d%n", assembly.getCurrentPass()); + AssemblyCompletionStatus status; + + while ((status = assembly.step()) == AssemblyCompletionStatus.PENDING) { + } + + return status == AssemblyCompletionStatus.STARTED_NEW_PASS; + } + +} diff --git a/src/main/java/org/reasm/batch/LocalFileFetcher.java b/src/main/java/org/reasm/batch/LocalFileFetcher.java new file mode 100644 index 0000000..2d36791 --- /dev/null +++ b/src/main/java/org/reasm/batch/LocalFileFetcher.java @@ -0,0 +1,57 @@ +package org.reasm.batch; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.reasm.FileFetcher; +import org.reasm.source.SourceFile; + +/** + * Fetches files from the local file system, relative to the directory of the main source file. + * + * @author Francis Gagné + */ +public class LocalFileFetcher implements FileFetcher { + + private final Path mainFileDirectoryPath; + + /** + * Initializes a new LocalFileFetcher. + * + * @param mainFilePath + * the path to the main source file + */ + public LocalFileFetcher(String mainFilePath) { + final Path mainFileDirectoryPath = Paths.get(mainFilePath).getParent(); + if (mainFileDirectoryPath != null && !Files.isDirectory(mainFileDirectoryPath)) { + throw new IllegalArgumentException("mainFilePath"); + } + + this.mainFileDirectoryPath = mainFileDirectoryPath; + } + + @Override + public byte[] fetchBinaryFile(String filePath) throws IOException { + return this.fetchFile(filePath); + } + + @Override + public SourceFile fetchSourceFile(String filePath) throws IOException { + return new SourceFile(new String(this.fetchFile(filePath), "UTF-8"), filePath); + } + + private byte[] fetchFile(String filePath) throws IOException { + return Files.readAllBytes(this.resolveInclude(filePath)); + } + + private Path resolveInclude(String filePath) { + if (this.mainFileDirectoryPath != null) { + return this.mainFileDirectoryPath.resolve(filePath).toAbsolutePath(); + } + + return Paths.get(filePath).toAbsolutePath(); + } + +} |