diff options
Diffstat (limited to 'src/main/java/no/eliashaugsbakk')
11 files changed, 162 insertions, 75 deletions
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java index 1c42673..cf50a74 100644 --- a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java +++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java @@ -2,6 +2,10 @@ package no.eliashaugsbakk.kompilator.IRGeneration; import java.util.ArrayList; import java.util.List; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Alloc; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Assign; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Call; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Instruction; import no.eliashaugsbakk.kompilator.parsing.AST; import no.eliashaugsbakk.kompilator.parsing.node.Program; import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression; @@ -15,7 +19,7 @@ import no.eliashaugsbakk.kompilator.parsing.node.statement.Statement; public class IRGenerator { private final AST ast; - private final List<Instr> ir = new ArrayList<>(); + private final List<Instruction> ir = new ArrayList<>(); private int tempCounter = 0; @@ -23,12 +27,13 @@ public class IRGenerator { this.ast = ast; } - public List<Instr> generate() { + public List<Instruction> generate() { Program program = (Program) ast.getRoot(); for (Statement stmt : program.statements) { generateStatement(stmt); } + ir.forEach(IO::println); return ir; } diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instr.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instr.java deleted file mode 100644 index 09ed4f6..0000000 --- a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instr.java +++ /dev/null @@ -1,33 +0,0 @@ -package no.eliashaugsbakk.kompilator.IRGeneration; - -import java.util.List; - -public sealed interface Instr permits Alloc, Assign, Call { -} - - -// allocate a new variable: set x: string = "Hello"; mut y: string?; -// -// Alloc("x", "string", false, "Hello") // Alloc("y", "string", true, "null") -record Alloc(String name, String type, boolean mutable, String initializer) implements Instr { - @Override - public String toString() { - return "Alloc[name=" + name + ", type=" + type + ", mutable=" + mutable + ", initializer=\"" + initializer + "\"]"; - } -} - - -// assign a value to a variable: x = "Hello"; -// Assign("x", "Hello") -record Assign(String name, String value) implements Instr { - @Override - public String toString() { - return "Assign[name=" + name + ", value=\"" + value + "\"]"; - } -} - - -// call a function: skriv("hello"); -// Call("skriv", ["t1"]) -record Call(String fn, List<String> args) implements Instr { -} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Alloc.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Alloc.java new file mode 100644 index 0000000..cda3a56 --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Alloc.java @@ -0,0 +1,12 @@ +package no.eliashaugsbakk.kompilator.IRGeneration.Instructions; + +// allocate a new variable: set x: string = "Hello"; mut y: string?; +// +// Alloc("x", "string", false, "Hello") // Alloc("y", "string", true, "null") +public record Alloc(String name, String type, boolean mutable, String initializer) implements Instruction { + @Override + public String toString() { + return "Alloc[name=" + name + ", type=" + type + ", mutable=" + mutable + ", initializer=\"" + + initializer + "\"]"; + } +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Assign.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Assign.java new file mode 100644 index 0000000..80a6d9e --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Assign.java @@ -0,0 +1,11 @@ +package no.eliashaugsbakk.kompilator.IRGeneration.Instructions; + +// assign a value to a variable: x = "Hello"; +// Assign("x", "Hello") +public record Assign(String name, String value) implements Instruction { + @Override + public String toString() { + return "Assign[name=" + name + ", value=\"" + value + "\"]"; + } +} + diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Call.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Call.java new file mode 100644 index 0000000..98f816c --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Call.java @@ -0,0 +1,8 @@ +package no.eliashaugsbakk.kompilator.IRGeneration.Instructions; + +import java.util.List; + +// call a function: skriv("hello"); +// Call("skriv", ["t1"]) +public record Call(String fn, List<String> args) implements Instruction { +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Instruction.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Instruction.java new file mode 100644 index 0000000..09796d6 --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/Instructions/Instruction.java @@ -0,0 +1,9 @@ +package no.eliashaugsbakk.kompilator.IRGeneration.Instructions; + +public sealed interface Instruction permits Alloc, Assign, Call { +} + + + + + diff --git a/src/main/java/no/eliashaugsbakk/kompilator/Main.java b/src/main/java/no/eliashaugsbakk/kompilator/Main.java index f59420c..8d6a5cf 100644 --- a/src/main/java/no/eliashaugsbakk/kompilator/Main.java +++ b/src/main/java/no/eliashaugsbakk/kompilator/Main.java @@ -5,6 +5,7 @@ import no.eliashaugsbakk.kompilator.IO.File; import no.eliashaugsbakk.kompilator.IO.FileReaderWriter; import no.eliashaugsbakk.kompilator.IO.FileReaderWriterException; import no.eliashaugsbakk.kompilator.IRGeneration.IRGenerator; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Instruction; import no.eliashaugsbakk.kompilator.asmGeneration.AssemblyBuilder; import no.eliashaugsbakk.kompilator.assembleAndLink.AssemblerAndLinker; import no.eliashaugsbakk.kompilator.parsing.AST; @@ -57,7 +58,7 @@ public class Main { System.exit(1); } - List<String> IR = new IRGenerator(ast).generate(); + List<Instruction> IR = new IRGenerator(ast).generate(); String assembly = new AssemblyBuilder().createAssembly(IR); diff --git a/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java b/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java index c039bcb..275a683 100644 --- a/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java +++ b/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java @@ -3,14 +3,22 @@ package no.eliashaugsbakk.kompilator.asmGeneration; import java.util.HashMap; import java.util.List; import java.util.Map; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Alloc; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Assign; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Call; +import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Instruction; public class AssemblyBuilder { - Map<String, String> stringVariables = new HashMap<>(); + Map<String, StringVar> stringVariables = new HashMap<>(); + + record StringVar(String value, boolean mutable) { + } StringBuilder finalAssembly; StringBuilder text; StringBuilder rodata; StringBuilder data; + StringBuilder bss; private final static String exit = """ mov rax, 60 @@ -26,66 +34,109 @@ public class AssemblyBuilder { this.rodata = new StringBuilder("\n.section .rodata\n"); // Global initialized variables, Read - Write this.data = new StringBuilder("\n.data\n"); + // Global uninitialized variables, Read - Write + this.bss = new StringBuilder("\n.section .bss\n"); } - public String createAssembly(List<String> IR) { - for (String line : IR) { - line = line.trim(); - - if (line.startsWith("print")) { - handlePrint(line); - } else if (line.contains("=")) { - handleAssignment(line); + public String createAssembly(List<Instruction> IR) { + for (Instruction inst : IR) { + if (inst instanceof Alloc alloc) { + handleAlloc(alloc); + } else if (inst instanceof Assign assign) { + handleAssign(assign); + } else if (inst instanceof Call call) { + handleCall(call); } } + this.text.append(exit); + this.finalAssembly.append(text); this.finalAssembly.append(rodata); this.finalAssembly.append(data); + this.finalAssembly.append(bss); + 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(":")); + private void handleAssign(Assign assign) { + String reassignLabel = assign.name() + "_reassign"; + this.stringVariables.put(assign.name(), new StringVar(assign.value(), true)); - String dataType = line.substring(line.indexOf(":") + 2, line.indexOf("=") - 1); + this.rodata.append(String.format(""" + %s: .ascii "%s" + """, reassignLabel, assign.value())); - String value = line.substring(line.indexOf("=") + 2); + this.text.append(String.format(""" + lea rax, [rip + %s] + mov [rip + %s], rax + """, reassignLabel, assign.name())); + } - if (dataType.contains("string")) { - assignString(name, value); + private void handleAlloc(Alloc alloc) { + this.stringVariables.put(alloc.name(), new StringVar(alloc.initializer(), alloc.mutable())); + if (alloc.mutable()) { + handleAllocMut(alloc); } else { - IO.println("err: Unknown data type: " + dataType); + handleAllocRO(alloc); } } - 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); + private void handleAllocMut(Alloc alloc) { + if (alloc.type() == null) { + this.bss.append(String.format(""" + %s: .skip 8 + """, alloc.name())); + } else if (alloc.type().equals("string")) { + String pointer = alloc.name() + "_ptr"; + this.rodata.append(String.format(""" + %s: .ascii "%s" + """, pointer, alloc.initializer())); + this.data.append(String.format(""" + %s: .quad %s + """, alloc.name(), pointer)); + } else { + throw new AssemblyBuilderException("Not a supported type"); + } } - 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."); + private void handleAllocRO(Alloc alloc) { + if (alloc.type().equals("string")) { + this.rodata.append(String.format(""" + %s: .ascii "%s" + """, alloc.name(), alloc.initializer())); + } else { + throw new AssemblyBuilderException("Not a supported type to skriv"); } + } - this.text.append(String.format(""" + private void handleCall(Call call) { + if (call.fn().equals("skriv")) { + handlePrint(call); + } else { + throw new AssemblyBuilderException("unknown call type: " + call.fn()); + } + } + + void handlePrint(Call call) { + if (call.args().size() != 1) { + throw new AssemblyBuilderException("Print only supports one argument"); + } + String variableName = call.args().getFirst(); + StringVar var = this.stringVariables.get(variableName); + int stringLength = var.value().length(); + + if (var.mutable()) { + this.text.append(String.format(""" + mov rsi, [rip + %s] + mov rax, 1 + mov rdi, 1 + mov rdx, %d + syscall + + """, variableName, stringLength)); + } else { + this.text.append(String.format(""" mov rax, 1 mov rdi, 1 lea rsi, %s @@ -93,5 +144,6 @@ public class AssemblyBuilder { syscall """, variableName, stringLength)); + } } } diff --git a/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilderException.java b/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilderException.java new file mode 100644 index 0000000..f477423 --- /dev/null +++ b/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilderException.java @@ -0,0 +1,7 @@ +package no.eliashaugsbakk.kompilator.asmGeneration; + +public class AssemblyBuilderException extends RuntimeException { + public AssemblyBuilderException(String message) { + super(message); + } +} diff --git a/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java b/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java index 5ab0b57..9abbc05 100644 --- a/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java +++ b/src/main/java/no/eliashaugsbakk/kompilator/assembleAndLink/AssemblerAndLinker.java @@ -45,6 +45,7 @@ public class AssemblerAndLinker { } // Clean up + /* try { fileReaderWriter.deleteFile(assemblyFileName); } catch (FileReaderWriterException e) { @@ -55,6 +56,8 @@ public class AssemblerAndLinker { } catch (FileReaderWriterException e) { IO.println("err: Could not delete assembled file: " + e.getMessage()); } + + */ } private static void runProcess(ProcessBuilder processBuilder) diff --git a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java index 9e9ebfd..fb3e09e 100644 --- a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java +++ b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java @@ -125,6 +125,18 @@ public class Lexer { state = NORMAL; position++; // skip closing " current = input.charAt(position); + } else if (current == '\\' && position + 1 < input.length()) { + // The source file contains \n as two characters: '\' and 'n'. + // We intercept the backslash and emit the character it represents. + position++; + current = input.charAt(position); + switch (current) { + case 'n' -> wordBuffer.append('\n'); + case 't' -> wordBuffer.append('\t'); + case 'r' -> wordBuffer.append('\r'); + case '\\' -> wordBuffer.append('\\'); + default -> wordBuffer.append(current); + } } else { wordBuffer.append(current); } |
