From 6dd4036bd828dac87b9f63d0a58693b30c46faed Mon Sep 17 00:00:00 2001 From: Elias Haugsbakk Date: Fri, 18 Sep 2026 19:10:54 +0200 Subject: split into separate packages/classes --- .../eliashaugsbakk/kompilator/AssemblyBuilder.java | 86 -------------- .../java/no/eliashaugsbakk/kompilator/IO/File.java | 4 + .../kompilator/IO/FileReaderWriter.java | 39 +++++++ .../kompilator/IO/FileReaderWriterException.java | 7 ++ .../java/no/eliashaugsbakk/kompilator/Main.java | 124 ++++----------------- .../kompilator/asmGeneration/AssemblyBuilder.java | 97 ++++++++++++++++ .../assembleAndLink/AssemblerAndLinker.java | 75 +++++++++++++ 7 files changed, 241 insertions(+), 191 deletions(-) delete mode 100644 src/main/java/no/eliashaugsbakk/kompilator/AssemblyBuilder.java create mode 100644 src/main/java/no/eliashaugsbakk/kompilator/IO/File.java create mode 100644 src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriter.java create mode 100644 src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriterException.java create mode 100644 src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java create mode 100644 src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java (limited to 'src/main/java/no') diff --git a/src/main/java/no/eliashaugsbakk/kompilator/AssemblyBuilder.java b/src/main/java/no/eliashaugsbakk/kompilator/AssemblyBuilder.java deleted file mode 100644 index 082f21d..0000000 --- a/src/main/java/no/eliashaugsbakk/kompilator/AssemblyBuilder.java +++ /dev/null @@ -1,86 +0,0 @@ -package no.eliashaugsbakk.kompilator; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class AssemblyBuilder { - Map stringVariables = new HashMap<>(); - - StringBuilder finalAssembly; - StringBuilder text; - StringBuilder rodata; - StringBuilder data; - - private final static String exit = """ - mov rax, 60 - xor rdi, rdi - syscall - """; - - public AssemblyBuilder() { - this.finalAssembly = new StringBuilder(".intel_syntax noprefix\n.global _start\n"); - // CPU instructions - this.text = new StringBuilder(".text\n_start:\n\n"); - // Read only data - this.rodata = new StringBuilder("\n.section .rodata\n"); - // Global initialized variables, Read - Write - this.data = new StringBuilder("\n.data\n"); - } - - String createAssembly(List IR) { - for (String line : IR) { - line = line.trim(); - - if (line.startsWith("print")) { - handlePrint(line); - } else if (line.contains("=")) { - handleAssignment(line); - } - } - this.text.append(exit); - this.finalAssembly.append(text); - this.finalAssembly.append(rodata); - this.finalAssembly.append(data); - return this.finalAssembly.toString(); - } - - private void handleAssignment(String line) { - /* Example IR: - variable_name: type = data - str1: string = "Hello world" - */ - String name = line.substring(0, line.indexOf(":")); - - String dataType = line.substring(line.indexOf(":") + 2, line.indexOf("=") - 1); - - String value = line.substring(line.indexOf("=") + 2); - - if (dataType.contains("string")) { - assignString(name, value); - } else { - IO.println("err: Unknown data type: " + dataType); - } - } - - private void assignString(String name, String value) { - value = value.substring(1, value.length() - 1); - this.rodata.append(String.format(""" - %s: .ascii "%s" - """, name, value)); - this.stringVariables.put(name, value); - } - - void handlePrint(String line) { - String variableName = line.substring(line.indexOf("(") + 1, line.lastIndexOf(")")); - - this.text.append(String.format(""" - mov rax, 1 - mov rdi, 1 - lea rsi, [%s] - lea rdx, [%d] - syscall - - """, variableName, this.stringVariables.get(variableName).length())); - } -} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IO/File.java b/src/main/java/no/eliashaugsbakk/kompilator/IO/File.java new file mode 100644 index 0000000..48f6edf --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IO/File.java @@ -0,0 +1,4 @@ +package no.eliashaugsbakk.kompilator.IO; + +public record File(String fileName, String fileBody) { +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriter.java b/src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriter.java new file mode 100644 index 0000000..b25b719 --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriter.java @@ -0,0 +1,39 @@ +package no.eliashaugsbakk.kompilator.IO; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class FileReaderWriter { + + public File readFile(String inputFilePath) throws FileReaderWriterException { + Path path = Path.of(inputFilePath); + + String rawName = path.getFileName().toString(); + int dotIndex = rawName.lastIndexOf('.'); + String fileName = (dotIndex == -1) ? rawName : rawName.substring(0, dotIndex); + + try { + String fileBody = Files.readString(path); + return new File(fileName, fileBody); + } catch (IOException e) { + throw new FileReaderWriterException("Failed to read file: " + inputFilePath, e); + } + } + + public void writeFile(File file) throws FileReaderWriterException { + try { + Files.writeString(Path.of(file.fileName()), file.fileBody()); + } catch (IOException e) { + throw new FileReaderWriterException("Failed to write file: " + file.fileName(), e); + } + } + + public void deleteFile(String filePath) throws FileReaderWriterException { + try { + Files.delete(Path.of(filePath)); + } catch (IOException e) { + throw new FileReaderWriterException("Could not delete file: " + filePath, e); + } + } +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriterException.java b/src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriterException.java new file mode 100644 index 0000000..1cc5bb8 --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IO/FileReaderWriterException.java @@ -0,0 +1,7 @@ +package no.eliashaugsbakk.kompilator.IO; + +public class FileReaderWriterException extends Exception { + public FileReaderWriterException(String message, Exception e) { + super("FileReaderWriter: " + message, e); + } +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/Main.java b/src/main/java/no/eliashaugsbakk/kompilator/Main.java index be5746c..aad3753 100644 --- a/src/main/java/no/eliashaugsbakk/kompilator/Main.java +++ b/src/main/java/no/eliashaugsbakk/kompilator/Main.java @@ -1,131 +1,45 @@ package no.eliashaugsbakk.kompilator; -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.List; +import no.eliashaugsbakk.kompilator.IO.File; +import no.eliashaugsbakk.kompilator.IO.FileReaderWriter; +import no.eliashaugsbakk.kompilator.IO.FileReaderWriterException; +import no.eliashaugsbakk.kompilator.asmGeneration.AssemblyBuilder; +import no.eliashaugsbakk.kompilator.assembleAndLink.AssemblerAndLinker; public class Main { public static final String programFileExtension = "spÄ"; - private record InputProgram(String programName, String programContent) { - } - static void main(String[] args) { if (args.length != 1) { IO.println("err: Only accepts one argument: The filename of the input program"); System.exit(1); } - InputProgram inputProgram = readInputProgram(args[0]); - - List IR = List.of(); - - String assembly = new AssemblyBuilder().createAssembly(IR); - - assemblingAndLInking(inputProgram.programName(), assembly); - - System.exit(0); - } - private static void assemblingAndLInking(String programName, String assembly) { - String assemblyFileName = programName + ".s"; - String assembledFileName = programName + ".o"; + String inputFileName = args[0]; - try { - FileWriter fw = new FileWriter(assemblyFileName); - fw.write(assembly); - fw.close(); - } catch (IOException e) { - IO.println("err: Could not write file: " + e.getMessage()); - } - - try { - // Assembling using: as -o program.o program.s - ProcessBuilder asPB = new ProcessBuilder("as", "-o", assembledFileName, assemblyFileName); - runProcess(asPB); - } catch (IOException e) { - IO.println("err: Failed to invoke GCC\n\n" + e); - } catch (InterruptedException e) { - IO.println("err: Failed to wait for gcc output\n\n" + e); - } - - try { - // Linking using GNU linker: ld -o program program.o - ProcessBuilder ldPB = new ProcessBuilder("ld", "-o", programName, assembledFileName); - runProcess(ldPB); - } catch (IOException e) { - IO.println("err: Failed to invoke GCC\n\n" + e); - } catch (InterruptedException e) { - IO.println("err: Failed to wait for gcc output\n\n" + e); + if (!inputFileName.endsWith("." + programFileExtension)) { + IO.println("err: Input file must use the file extension: " + programFileExtension); + System.exit(1); } - // Clean up + File inputProgram = null; + FileReaderWriter fileReaderWriter = new FileReaderWriter(); try { - Files.delete(Path.of(assemblyFileName)); - } catch (IOException e) { - IO.println("err: Could not delete assembly file: " + e.getMessage()); - } - try { - Files.delete(Path.of(assembledFileName)); - } catch (IOException e) { - IO.println("err: Could not delete assembled file: " + e.getMessage()); - } - } - - private static void runProcess(ProcessBuilder processBuilder) - throws IOException, InterruptedException { - Process process = processBuilder.start(); - process.waitFor(); - if (process.exitValue() != 0) { - BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(process.getErrorStream())); - - String line; - while ((line = bufferedReader.readLine()) != null) { - System.out.println(line); - } - bufferedReader.close(); - } - } - - private static InputProgram readInputProgram(String inputFilePath) { - if (!inputFilePath.endsWith("." + programFileExtension)) { - IO.println("err: Input file must use the file extension: " + programFileExtension); + inputProgram = fileReaderWriter.readFile(inputFileName); + } catch (FileReaderWriterException e) { + IO.println("Could not read input file: " + inputFileName + "\n\n" + e.getMessage()); System.exit(1); } - String programName = - inputFilePath.substring(inputFilePath.lastIndexOf("/") + 1, inputFilePath.lastIndexOf(".")); - - String programContent = null; - - try (BufferedReader br = new BufferedReader(new FileReader(inputFilePath))) { - StringBuilder sb = new StringBuilder(); - String line = br.readLine(); + List IR = List.of(); - while (line != null) { - sb.append(line); - sb.append(System.lineSeparator()); - line = br.readLine(); - } - programContent = sb.toString(); - } catch (FileNotFoundException e) { - IO.println("err: File not found"); - } catch (IOException e) { - IO.println(e.getMessage()); - } + String assembly = new AssemblyBuilder().createAssembly(IR); - if (programContent == null) { - IO.println("err: Could not read input file"); - System.exit(1); - } + AssemblerAndLinker assemblerAndLinker = new AssemblerAndLinker(); + assemblerAndLinker.assembleAndLink(inputProgram.fileName(), assembly); - return new InputProgram(programName, programContent); + System.exit(0); } } diff --git a/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java b/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java new file mode 100644 index 0000000..94a753b --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java @@ -0,0 +1,97 @@ +package no.eliashaugsbakk.kompilator.asmGeneration; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AssemblyBuilder { + Map stringVariables = new HashMap<>(); + + StringBuilder finalAssembly; + StringBuilder text; + StringBuilder rodata; + StringBuilder data; + + private final static String exit = """ + mov rax, 60 + xor rdi, rdi + syscall + """; + + public AssemblyBuilder() { + this.finalAssembly = new StringBuilder(".intel_syntax noprefix\n.global _start\n"); + // CPU instructions + this.text = new StringBuilder(".text\n_start:\n\n"); + // Read only data + this.rodata = new StringBuilder("\n.section .rodata\n"); + // Global initialized variables, Read - Write + this.data = new StringBuilder("\n.data\n"); + } + + public String createAssembly(List IR) { + for (String line : IR) { + line = line.trim(); + + if (line.startsWith("print")) { + handlePrint(line); + } else if (line.contains("=")) { + handleAssignment(line); + } + } + this.text.append(exit); + this.finalAssembly.append(text); + this.finalAssembly.append(rodata); + this.finalAssembly.append(data); + return this.finalAssembly.toString(); + } + + private void handleAssignment(String line) { + /* Example IR: + variable_name: type = data + str1: string = "Hello world" + */ + String name = line.substring(0, line.indexOf(":")); + + String dataType = line.substring(line.indexOf(":") + 2, line.indexOf("=") - 1); + + String value = line.substring(line.indexOf("=") + 2); + + if (dataType.contains("string")) { + assignString(name, value); + } else { + IO.println("err: Unknown data type: " + dataType); + } + } + + private void assignString(String name, String value) { + value = value.substring(1, value.length() - 1); + this.rodata.append(String.format(""" + %s: .ascii "%s" + """, name, value)); + this.stringVariables.put(name, value); + } + + void handlePrint(String line) { + /* Takes in a print statement and constructs the assembly + example input: + print(x) + */ + String variableName = line.substring(line.indexOf("(") + 1, line.lastIndexOf(")")); + + int stringLength = 0; + try { + stringLength = this.stringVariables.get(variableName).length(); + } catch (RuntimeException e) { + IO.println("No variable " + variableName + " initialized in IR."); + } + + this.text.append(String.format(""" + mov rax, 1 + mov rdi, 1 + lea rsi, [%s] + lea rdx, [%d] + syscall + + """, variableName, stringLength)); + } +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java b/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java new file mode 100644 index 0000000..5ab0b57 --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java @@ -0,0 +1,75 @@ +package no.eliashaugsbakk.kompilator.assembleAndLink; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import no.eliashaugsbakk.kompilator.IO.File; +import no.eliashaugsbakk.kompilator.IO.FileReaderWriter; +import no.eliashaugsbakk.kompilator.IO.FileReaderWriterException; + +public class AssemblerAndLinker { + private final FileReaderWriter fileReaderWriter; + + public AssemblerAndLinker() { + fileReaderWriter = new FileReaderWriter(); + } + + public void assembleAndLink(String programName, String assembly) { + String assemblyFileName = programName + ".s"; + String assembledFileName = programName + ".o"; + + try { + fileReaderWriter.writeFile(new File(assemblyFileName, assembly)); + } catch (FileReaderWriterException e) { + IO.println("err: Could not write file: " + e.getMessage()); + } + + try { + // Assembling using: as -o program.o program.s + ProcessBuilder asPB = new ProcessBuilder("as", "-o", assembledFileName, assemblyFileName); + runProcess(asPB); + } catch (IOException e) { + IO.println("err: Failed to invoke GCC\n\n" + e); + } catch (InterruptedException e) { + IO.println("err: Failed to wait for gcc output\n\n" + e); + } + + try { + // Linking using GNU linker: ld -o program program.o + ProcessBuilder ldPB = new ProcessBuilder("ld", "-o", programName, assembledFileName); + runProcess(ldPB); + } catch (IOException e) { + IO.println("err: Failed to invoke GCC\n\n" + e); + } catch (InterruptedException e) { + IO.println("err: Failed to wait for gcc output\n\n" + e); + } + + // Clean up + try { + fileReaderWriter.deleteFile(assemblyFileName); + } catch (FileReaderWriterException e) { + IO.println("err: Could not delete assembly file: " + e.getMessage()); + } + try { + fileReaderWriter.deleteFile(assembledFileName); + } catch (FileReaderWriterException e) { + IO.println("err: Could not delete assembled file: " + e.getMessage()); + } + } + + private static void runProcess(ProcessBuilder processBuilder) + throws IOException, InterruptedException { + Process process = processBuilder.start(); + process.waitFor(); + if (process.exitValue() != 0) { + BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(process.getErrorStream())); + + String line; + while ((line = bufferedReader.readLine()) != null) { + System.out.println(line); + } + bufferedReader.close(); + } + } +} -- cgit v1.2.3