diff --git a/src/main/java/net/darmo_creations/ti83_compiler/BinaryFile.java b/src/main/java/net/darmo_creations/ti83_compiler/BinaryFile.java index 574e631..d3fee61 100644 --- a/src/main/java/net/darmo_creations/ti83_compiler/BinaryFile.java +++ b/src/main/java/net/darmo_creations/ti83_compiler/BinaryFile.java @@ -1,11 +1,11 @@ package net.darmo_creations.ti83_compiler; +import net.darmo_creations.ti83_compiler.utils.ArraysUtil; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import net.darmo_creations.ti83_compiler.utils.ArraysUtil; - /** * This class represents a .8xp program file. * @@ -18,14 +18,14 @@ public class BinaryFile { private boolean editable; public static final String FORMAT = "8xp"; - public static final String HEADER = "**TI83F*\u001a\n\0"; + public static final String HEADER = "**TI83F*\u001a\n\n"; public static final String COMMENT = "Created by Java TI-Compiler"; public BinaryFile(String path, String name, byte[] data, boolean editable) { this.path = path; - setName(name); - setData(data); - setEditable(editable); + this.setName(name); + this.setData(data); + this.setEditable(editable); } public String getPath() { @@ -62,7 +62,7 @@ public void setName(String name) { /** * Changes the program's data.
* NOTE: tokens' validity is not checked! - * + * * @param data the program's data */ public void setData(byte[] data) { @@ -71,7 +71,7 @@ public void setData(byte[] data) { /** * Writes the file. - * + * * @throws IOException if the file could not be written */ public void writeFile() throws IOException { @@ -96,7 +96,7 @@ public void writeFile() throws IOException { header[57] = (byte) (l & 0x00FF); header[58] = (byte) ((l & 0xFF00) >> 8); // Protection - header[59] = (byte) (isEditable() ? 0x05 : 0x06); + header[59] = (byte) (this.isEditable() ? 0x05 : 0x06); // Program name ArraysUtil.stringCopy(this.name, header, 60, 8); // More things @@ -125,7 +125,7 @@ public void writeFile() throws IOException { content[content.length - 2] = (byte) (l & 0x00FF); content[content.length - 1] = (byte) ((l & 0xFF00) >> 8); - try (FileOutputStream fos = new FileOutputStream(new File(getAbsolutePath()))) { + try (FileOutputStream fos = new FileOutputStream(this.getAbsolutePath())) { fos.write(content); } } diff --git a/src/main/java/net/darmo_creations/ti83_compiler/Main.java b/src/main/java/net/darmo_creations/ti83_compiler/Main.java index f236bda..0203300 100644 --- a/src/main/java/net/darmo_creations/ti83_compiler/Main.java +++ b/src/main/java/net/darmo_creations/ti83_compiler/Main.java @@ -1,12 +1,12 @@ package net.darmo_creations.ti83_compiler; +import net.darmo_creations.ti83_compiler.compilers.Compiler; +import net.darmo_creations.ti83_compiler.compilers.Decompiler; + import java.io.PrintStream; import java.util.Arrays; import java.util.List; -import net.darmo_creations.ti83_compiler.compilers.Compiler; -import net.darmo_creations.ti83_compiler.compilers.Decompiler; - public class Main { public static void main(String[] args) { System.out.println("** TI-83 Compiler/Decompiler v1.2 **"); @@ -16,8 +16,7 @@ public static void main(String[] args) { try { if (argz.size() == 1 && argz.get(0).equals("-h")) { printUsage(false); - } - else { + } else { if (!argz.contains("-f") || argz.indexOf("-f") == argz.size() - 1) { throw new IllegalArgumentException("No file specified!"); } @@ -29,41 +28,39 @@ public static void main(String[] args) { boolean optimise = argz.contains("-O"); boolean editable = !argz.contains("-L"); new Compiler().compile(file, optimise, editable); - } - else { + } else { Decompiler decompiler = new Decompiler(); int indexLanguage = argz.indexOf("-u") + 1; - if (indexLanguage > argz.size() - 1) + if (indexLanguage > argz.size() - 1) { throw new IllegalArgumentException("Missing target language!"); + } String lang = argz.get(indexLanguage); if (argz.contains("-i")) { int indexIndent = argz.indexOf("-i") + 1; - if (indexIndent > argz.size() - 1) + if (indexIndent > argz.size() - 1) { throw new IllegalArgumentException("Missing indent size!"); + } int indentSize; try { indentSize = Integer.parseInt(argz.get(indexIndent)); - if (indentSize < 0) + if (indentSize < 0) { throw new IllegalArgumentException("Indent size must be a positive integer!"); + } decompiler.decompile(file, lang, indentSize); - } - catch (NumberFormatException ex) { + } catch (NumberFormatException ex) { throw new IllegalArgumentException("Indent size must be an integer!", ex); } - } - else { + } else { decompiler.decompile(file, lang); } } } - } - catch (IllegalArgumentException ex) { + } catch (IllegalArgumentException ex) { System.err.println(ex.getMessage()); printUsage(true); - } - catch (Exception ex) { + } catch (Exception ex) { System.err.println(ex.getMessage()); } } @@ -85,4 +82,4 @@ private static void printUsage(boolean error) { out.println("\tshow this help"); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/darmo_creations/ti83_compiler/compilers/BinaryFileParser.java b/src/main/java/net/darmo_creations/ti83_compiler/compilers/BinaryFileParser.java index 065a376..8ca1ae9 100644 --- a/src/main/java/net/darmo_creations/ti83_compiler/compilers/BinaryFileParser.java +++ b/src/main/java/net/darmo_creations/ti83_compiler/compilers/BinaryFileParser.java @@ -1,14 +1,14 @@ package net.darmo_creations.ti83_compiler.compilers; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import net.darmo_creations.ti83_compiler.BinaryFile; import net.darmo_creations.ti83_compiler.exceptions.FileFormatException; import net.darmo_creations.ti83_compiler.exceptions.UnknownTokenException; import net.darmo_creations.ti83_compiler.utils.ArraysUtil; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * This class parses a .8xp file into a source code text file. * @@ -18,8 +18,8 @@ class BinaryFileParser { public static final int DEFAULT_INDENT_SIZE = 4; private final byte[] header; - private String language; - private int indentSize; + private final String language; + private final int indentSize; public BinaryFileParser(String language, int indentSize) { this.header = new byte[11]; @@ -31,35 +31,34 @@ public BinaryFileParser(String language, int indentSize) { /** * Parses a .8xp file then returns the corresponding source code. The first line is the program's * name. - * + * * @param content the content to parse * @return the program's source code - * @throws UnknownTokenException if the parser stumbles upon an unknown token * @throws FileFormatException if the file is corrupted */ - public String[] parse(byte[] content) throws UnknownTokenException, FileFormatException { + public String[] parse(byte[] content) throws FileFormatException { List lines = new ArrayList<>(); try { // Checks the header. - if (!checkHeader(content)) { + if (!this.checkHeader(content)) { throw new FileFormatException("Invalid header."); } - int length = toInt(content[0x48], content[0x49]); + int length = this.toInt(content[0x48], content[0x49]); // Checks the length. - if (toInt(content[0x35], content[0x36]) - 19 != length // - || toInt(content[0x39], content[0x3A]) - 2 != length // - || toInt(content[0x46], content[0x47]) - 2 != length) { + if (this.toInt(content[0x35], content[0x36]) - 19 != length // + || this.toInt(content[0x39], content[0x3A]) - 2 != length // + || this.toInt(content[0x46], content[0x47]) - 2 != length) { throw new FileFormatException("Invalid length."); } // Gets the name. - lines.add(getName(content)); + lines.add(this.getName(content)); // Checksum. - if (!checkSum(content)) { + if (!this.checkSum(content)) { System.err.println("WARNING: invalid checksum, the file may be corrupted."); System.err.println("We will still attempt to open it."); System.err.println(); @@ -78,20 +77,19 @@ public String[] parse(byte[] content) throws UnknownTokenException, FileFormatEx lines.add(indent + currentLine); if (increaseIndent) { - indent = addIndent(indent); + indent = this.addIndent(indent); increaseIndent = false; } if (newLineAfterIf) { - indent = removeIndent(indent); + indent = this.removeIndent(indent); newLineAfterIf = false; } if (inIfWithoutThen) { - indent = addIndent(indent); + indent = this.addIndent(indent); inIfWithoutThen = false; newLineAfterIf = true; } currentLine = ""; - found = true; continue; } @@ -115,7 +113,7 @@ else if (token.getInstruction().equals("If ")) { // else, decrease current indent then re-increase indent. else if (token.getInstruction().equals("Then")) { if (newLineAfterIf) { - indent = removeIndent(indent); + indent = this.removeIndent(indent); newLineAfterIf = false; } inIfWithoutThen = false; @@ -123,12 +121,12 @@ else if (token.getInstruction().equals("Then")) { } // Else detected. Decrease current indent then increase indent. else if (token.getInstruction().equals("Else")) { - indent = removeIndent(indent); + indent = this.removeIndent(indent); increaseIndent = true; } // End detected. Decrease current indent. else if (token.getInstruction().equals("End")) { - indent = removeIndent(indent); + indent = this.removeIndent(indent); } // If the instruction after an if is on the same line. // E.g.: 'If B:Disp B' @@ -153,10 +151,9 @@ else if (inIfWithoutThen && token.getInstruction().equals(":")) { } } - return lines.toArray(new String[lines.size()]); - } - catch (Exception ex) { - throw new FileFormatException("Corrupted file.", ex); + return lines.toArray(new String[0]); + } catch (Exception ex) { + throw new FileFormatException("Corrupted file: " + ex.getMessage(), ex); } } @@ -165,9 +162,7 @@ else if (inIfWithoutThen && token.getInstruction().equals(":")) { */ private boolean checkHeader(final byte[] bytes) { byte[] header = new byte[11]; - System.arraycopy(bytes, 0, header, 0, 11); - return Arrays.equals(header, this.header); } @@ -175,13 +170,13 @@ private boolean checkHeader(final byte[] bytes) { * Returns the program's name. */ private String getName(byte[] bytes) { - String name = ""; + StringBuilder name = new StringBuilder(); for (int i = 0x3C; i < 0x3C + 8 && bytes[i] != 0; i++) { - name += (bytes[i] == 0x5B) ? 'θ' : (char) bytes[i]; + name.append((bytes[i] == 0x5B) ? 'θ' : (char) bytes[i]); } - return name; + return name.toString(); } /** @@ -194,7 +189,7 @@ private boolean checkSum(byte[] bytes) { sum += (bytes[i] < 0) ? bytes[i] + 256 : bytes[i]; } - return (sum & 0xFFFF) == toInt(bytes[bytes.length - 2], bytes[bytes.length - 1]); + return (sum & 0xFFFF) == this.toInt(bytes[bytes.length - 2], bytes[bytes.length - 1]); } /** @@ -218,8 +213,9 @@ private String addIndent(String indent) { * Removes an indent level. */ private String removeIndent(String indent) { - if (indent.length() < this.indentSize) + if (indent.length() < this.indentSize) { return ""; + } return indent.substring(0, indent.length() - this.indentSize); } } diff --git a/src/main/java/net/darmo_creations/ti83_compiler/compilers/Compiler.java b/src/main/java/net/darmo_creations/ti83_compiler/compilers/Compiler.java index 099f67c..54eb133 100644 --- a/src/main/java/net/darmo_creations/ti83_compiler/compilers/Compiler.java +++ b/src/main/java/net/darmo_creations/ti83_compiler/compilers/Compiler.java @@ -1,5 +1,9 @@ package net.darmo_creations.ti83_compiler.compilers; +import net.darmo_creations.ti83_compiler.BinaryFile; +import net.darmo_creations.ti83_compiler.exceptions.FileFormatException; +import net.darmo_creations.ti83_compiler.exceptions.UnknownInstructionException; + import java.io.File; import java.io.IOException; import java.nio.charset.MalformedInputException; @@ -8,15 +12,11 @@ import java.nio.file.Paths; import java.util.List; -import net.darmo_creations.ti83_compiler.BinaryFile; -import net.darmo_creations.ti83_compiler.exceptions.FileFormatException; -import net.darmo_creations.ti83_compiler.exceptions.UnknownInstructionException; - public class Compiler { /** * Compiles the given file. - * - * @param path the path to the file + * + * @param path the path to the file * @param optimise if true, the compiled program will be optimised * @throws IOException * @throws FileFormatException @@ -26,28 +26,27 @@ public void compile(String path, boolean optimise, boolean editable) throws IOEx String progName; String[] srcCode; - f = new File(path); + f = new File(path).getAbsoluteFile(); - if (!f.getName().substring(f.getName().indexOf('.') + 1).toUpperCase().matches("TI83(EN|FR)?")) + if (!f.getName().substring(f.getName().indexOf('.') + 1).toUpperCase().matches("TI83(EN|FR)?")) { throw new FileFormatException("Unsupported file format."); - if (!f.exists()) + } + if (!f.exists()) { throw new IOException("File does not exist!"); + } progName = f.getName().substring(0, f.getName().indexOf(".")).toUpperCase(); - srcCode = getSourceCode(f); + srcCode = this.getSourceCode(f); byte[] data; try { data = new SourceCodeParser().parse(srcCode, optimise); - } - catch (UnknownInstructionException ex) { - String offset = "\n"; - + } catch (UnknownInstructionException ex) { + StringBuilder offset = new StringBuilder("\n"); for (int i = 0; i < ex.getColumnOffset() - 1; i++) { - offset += " "; + offset.append(" "); } - throw new RuntimeException("Unknown token at line " + ex.getErrorOffset() + ":\n" + ex.getMessage() + offset + "^", ex); } @@ -55,8 +54,7 @@ public void compile(String path, boolean optimise, boolean editable) throws IOEx try { bf.writeFile(); - } - catch (IOException ex) { + } catch (IOException ex) { throw new IOException("Could not write file " + ex.getMessage(), ex); } @@ -70,13 +68,10 @@ public void compile(String path, boolean optimise, boolean editable) throws IOEx private String[] getSourceCode(File file) throws IOException { try { List lines = Files.readAllLines(Paths.get(file.getAbsolutePath())); - - return lines.toArray(new String[lines.size()]); - } - catch (NullPointerException | InvalidPathException | SecurityException ex) { + return lines.toArray(new String[0]); + } catch (NullPointerException | InvalidPathException | SecurityException ex) { throw new IOException("Could not open file.", ex); - } - catch (MalformedInputException ex) { + } catch (MalformedInputException ex) { throw new IOException("Unsupported encoding! Source files must be encoded in UTF-8."); } } diff --git a/src/main/java/net/darmo_creations/ti83_compiler/compilers/Decompiler.java b/src/main/java/net/darmo_creations/ti83_compiler/compilers/Decompiler.java index 839cb81..c2dc736 100644 --- a/src/main/java/net/darmo_creations/ti83_compiler/compilers/Decompiler.java +++ b/src/main/java/net/darmo_creations/ti83_compiler/compilers/Decompiler.java @@ -29,7 +29,7 @@ public void decompile(String path, String language) throws FileFormatException, * @param indentSize indentation size * @throws FileFormatException */ - public void decompile(String path, String language, int indentSize) throws FileFormatException, UnknownTokenException, IOException { + public void decompile(String path, String language, int indentSize) throws FileFormatException, IOException { File f; String progName; String[] lines; @@ -38,7 +38,7 @@ public void decompile(String path, String language, int indentSize) throws FileF throw new IllegalArgumentException("Unknown language " + language); } - f = new File(path); + f = new File(path).getAbsoluteFile(); if (!f.getName().substring(f.getName().indexOf('.') + 1).toUpperCase().matches("8XP")) throw new FileFormatException("Unsupported file format."); diff --git a/src/main/java/net/darmo_creations/ti83_compiler/compilers/SourceCodeParser.java b/src/main/java/net/darmo_creations/ti83_compiler/compilers/SourceCodeParser.java index 81580ff..61293da 100644 --- a/src/main/java/net/darmo_creations/ti83_compiler/compilers/SourceCodeParser.java +++ b/src/main/java/net/darmo_creations/ti83_compiler/compilers/SourceCodeParser.java @@ -1,31 +1,32 @@ package net.darmo_creations.ti83_compiler.compilers; +import net.darmo_creations.ti83_compiler.exceptions.UnknownInstructionException; + import java.util.ArrayList; import java.util.List; -import net.darmo_creations.ti83_compiler.exceptions.UnknownInstructionException; - class SourceCodeParser { /** * Parses the given source code then returns the corresponding tokens. - * - * @param srcCode the source code + * + * @param srcCode the source code * @param optimise if true, the compiled program will be optimised * @return the tokens * @throws UnknownInstructionException */ public byte[] parse(String[] srcCode, boolean optimise) throws UnknownInstructionException { - trim(srcCode); + this.trim(srcCode); // Used to show the parse errors. final String[] errorSource = new String[srcCode.length]; System.arraycopy(srcCode, 0, errorSource, 0, srcCode.length); - replaceTags(srcCode); + this.replaceTags(srcCode); - List tokens = extractTokens(srcCode, errorSource); + List tokens = this.extractTokens(srcCode, errorSource); - if (optimise) - optimise(tokens); + if (optimise) { + this.optimise(tokens); + } List bytes = new ArrayList<>(); @@ -70,13 +71,11 @@ private List extractTokens(String[] srcCode, String[] errorSource) throws if (line.charAt(index) == '#') { index = line.length(); found = true; - } - else if (line.charAt(index) == '\\') { + } else if (line.charAt(index) == '\\') { escapeNext = true; index++; found = true; - } - else { + } else { // Parsing one token for (Token token : Tokens.TOKENS) { String instr = token.getInstruction(); @@ -85,7 +84,7 @@ else if (line.charAt(index) == '\\') { continue; } - if (line.substring(index, index + instr.length()).equals(instr)) { + if (line.startsWith(instr, index)) { tokens.add(token); escapeNext = false; @@ -134,8 +133,9 @@ private void optimise(List tokens) { ignoreToken |= !inString && (token.equals(closedParenthesis) && next != null && next.equals(columns) || token.equals(star) && (previous != null && !Tokens.isDigit(previous) || next != null && !Tokens.isDigit(next))); - if (!inString && token.equals(star) && (previous != null && Tokens.isNewLine(previous) || next != null && Tokens.isNewLine(next))) + if (!inString && token.equals(star) && (previous != null && Tokens.isNewLine(previous) || next != null && Tokens.isNewLine(next))) { ignoreToken = false; + } if (ignoreToken) { changed = true; @@ -143,10 +143,12 @@ private void optimise(List tokens) { i--; } - if (token.equals(quote)) + if (token.equals(quote)) { inString = !inString; - if (token.equals(Tokens.LINE_END) || token.equals(arrow)) + } + if (token.equals(Tokens.LINE_END) || token.equals(arrow)) { inString = false; + } } } while (changed); }