use condegen instance again instead of static => cleanup method arguments
This commit is contained in:
@ -78,7 +78,8 @@ public final class StupsCompiler {
|
|||||||
|
|
||||||
// Codegeneration + Output
|
// Codegeneration + Output
|
||||||
final String outputName = filename.replaceFirst("stups", "j");
|
final String outputName = filename.replaceFirst("stups", "j");
|
||||||
final StringBuilder jasmin = CodeGenerator.generateCode(tree, filename, nodeTable);
|
final CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
||||||
|
final StringBuilder jasmin = gen.generateCode(filename);
|
||||||
try {
|
try {
|
||||||
final Path outputFile = Paths.get(System.getProperty("user.dir") + "/" + outputName);
|
final Path outputFile = Paths.get(System.getProperty("user.dir") + "/" + outputName);
|
||||||
Files.writeString(outputFile, jasmin.toString());
|
Files.writeString(outputFile, jasmin.toString());
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package codegen;
|
|||||||
|
|
||||||
import parser.ast.AST;
|
import parser.ast.AST;
|
||||||
import parser.ast.ASTNode;
|
import parser.ast.ASTNode;
|
||||||
import util.Logger;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -13,31 +12,50 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
|
import static util.Logger.log;
|
||||||
|
|
||||||
public final class CodeGenerator {
|
public final class CodeGenerator {
|
||||||
|
|
||||||
private static final Map<String, Method> nodeMap;
|
private static final Map<String, Method> methodMap;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Map<String, Method> map;
|
Map<String, Method> map;
|
||||||
try {
|
try {
|
||||||
final Class<?> gen = CodeGenerator.class;
|
final Class<?> gen = CodeGenerator.class;
|
||||||
map = Map.ofEntries(
|
map = Map.ofEntries(
|
||||||
entry("assignment", gen.getDeclaredMethod("assign", ASTNode.class, StringBuilder.class, Map.class, int.class)),
|
entry("assignment", gen.getDeclaredMethod("assign", ASTNode.class)),
|
||||||
entry("expr", gen.getDeclaredMethod("expr", ASTNode.class, StringBuilder.class, Map.class, int.class)),
|
entry("expr", gen.getDeclaredMethod("expr", ASTNode.class)),
|
||||||
entry("INTEGER_LIT", gen.getDeclaredMethod("literal", ASTNode.class, StringBuilder.class, Map.class, int.class)),
|
entry("INTEGER_LIT", gen.getDeclaredMethod("literal", ASTNode.class)),
|
||||||
entry("IDENTIFIER", gen.getDeclaredMethod("identifier", ASTNode.class, StringBuilder.class, Map.class, int.class)),
|
entry("BOOLEAN_LIT", gen.getDeclaredMethod("literal", ASTNode.class)),
|
||||||
entry("print", gen.getDeclaredMethod("println", ASTNode.class, StringBuilder.class, Map.class, int.class)),
|
entry("STRING_LIT", gen.getDeclaredMethod("literal", ASTNode.class)),
|
||||||
entry("cond", gen.getDeclaredMethod("cond", ASTNode.class, StringBuilder.class, Map.class, int.class))
|
entry("IDENTIFIER", gen.getDeclaredMethod("identifier", ASTNode.class)),
|
||||||
|
entry("print", gen.getDeclaredMethod("println", ASTNode.class)),
|
||||||
|
entry("cond", gen.getDeclaredMethod("cond", ASTNode.class))
|
||||||
);
|
);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
map = null;
|
map = null;
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
nodeMap = map;
|
methodMap = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeGenerator() {}
|
private final Map<String, Integer> varMap;
|
||||||
|
private final Map<ASTNode, String> nodeTypeMap;
|
||||||
|
private final StringBuilder jasmin;
|
||||||
|
private final AST tree;
|
||||||
|
|
||||||
|
private int labelCounter;
|
||||||
|
|
||||||
|
private CodeGenerator(Map<String, Integer> varMap, AST tree, Map<ASTNode, String> nodeTypeMap) {
|
||||||
|
this.varMap = varMap;
|
||||||
|
this.tree = tree;
|
||||||
|
this.nodeTypeMap = nodeTypeMap;
|
||||||
|
this.jasmin = new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CodeGenerator fromAST(AST tree, Map<ASTNode, String> nodeTypeMap) {
|
||||||
|
return new CodeGenerator(varMapFromAST(tree), tree, nodeTypeMap);
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<String, Integer> varMapFromAST(AST tree) {
|
private static Map<String, Integer> varMapFromAST(AST tree) {
|
||||||
final Map<String, Integer> varMap = new HashMap<>();
|
final Map<String, Integer> varMap = new HashMap<>();
|
||||||
@ -52,7 +70,7 @@ public final class CodeGenerator {
|
|||||||
if ("declaration".equals(current.getName())) {
|
if ("declaration".equals(current.getName())) {
|
||||||
varCount++;
|
varCount++;
|
||||||
varMap.put(current.getChildren().get(0).getValue(), varCount);
|
varMap.put(current.getChildren().get(0).getValue(), varCount);
|
||||||
Logger.log("New local " + current.getValue() + " variable "
|
log("New local " + current.getValue() + " variable "
|
||||||
+ current.getChildren().get(0).getValue()
|
+ current.getChildren().get(0).getValue()
|
||||||
+ " assigned to slot " + varCount + ".");
|
+ " assigned to slot " + varCount + ".");
|
||||||
}
|
}
|
||||||
@ -63,20 +81,20 @@ public final class CodeGenerator {
|
|||||||
return Collections.unmodifiableMap(varMap);
|
return Collections.unmodifiableMap(varMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateHeader(AST ast, String source, StringBuilder jasmin) {
|
private void generateHeader(String source) {
|
||||||
System.out.println(" - Generating Jasmin Assembler...");
|
System.out.println(" - Generating Jasmin Assembler...");
|
||||||
final String clazz = ast.getRoot().getChildren().get(1).getValue();
|
final String clazz = this.tree.getRoot().getChildren().get(1).getValue();
|
||||||
|
|
||||||
jasmin.append(".bytecode 55.0\n")
|
this.jasmin.append(".bytecode 55.0\n")
|
||||||
.append(".source ").append(source).append("\n")
|
.append(".source ").append(source).append("\n")
|
||||||
.append(".class public ").append(clazz).append("\n")
|
.append(".class public ").append(clazz).append("\n")
|
||||||
.append(".super java/lang/Object\n");
|
.append(".super java/lang/Object\n");
|
||||||
|
|
||||||
System.out.println("Code-generation successfull.");
|
log("Generated Jasmin Header.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateConstructor(StringBuilder jasmin) {
|
private void generateConstructor() {
|
||||||
jasmin.append(".method public <init>()V\n")
|
this.jasmin.append(".method public <init>()V\n")
|
||||||
.append("\t.limit stack 1\n")
|
.append("\t.limit stack 1\n")
|
||||||
.append("\t.limit locals 1\n")
|
.append("\t.limit locals 1\n")
|
||||||
.append("\t.line 1\n")
|
.append("\t.line 1\n")
|
||||||
@ -85,94 +103,97 @@ public final class CodeGenerator {
|
|||||||
.append("\t\treturn\n")
|
.append("\t\treturn\n")
|
||||||
.append(".end method\n\n");
|
.append(".end method\n\n");
|
||||||
|
|
||||||
Logger.log("Generated Jasmin Init.");
|
log("Generated Jasmin Constructor.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringBuilder generateCode(AST ast, String source, Map<ASTNode, String> nodeTable) {
|
public StringBuilder generateCode(String source) {
|
||||||
final StringBuilder jasmin = new StringBuilder();
|
this.generateHeader(source);
|
||||||
final Map<String, Integer> varMap = varMapFromAST(ast);
|
this.generateConstructor();
|
||||||
|
this.generateMain();
|
||||||
|
|
||||||
generateHeader(ast, source, jasmin);
|
log("Jasmin Assembler:\n" + this.jasmin);
|
||||||
generateConstructor(jasmin);
|
System.out.println("Code-generation successfull.");
|
||||||
generateMain(ast, jasmin, varMap, nodeTable);
|
|
||||||
|
|
||||||
Logger.log("Jasmin Assembler:\n" + jasmin);
|
return this.jasmin;
|
||||||
|
|
||||||
return jasmin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Indentation?
|
// TODO: Indentation?
|
||||||
// TODO: Stack size
|
// TODO: Stack size
|
||||||
// TODO: Typen
|
// TODO: Typen
|
||||||
// TODO: Variablengröße
|
// TODO: Variablengröße
|
||||||
private static void generateMain(AST ast, StringBuilder jasmin, Map<String, Integer> varMap, Map<ASTNode, String> nodeTable) {
|
private void generateMain() {
|
||||||
jasmin.append(".method public static main([Ljava/lang/String;)V\n")
|
this.jasmin.append(".method public static main([Ljava/lang/String;)V\n")
|
||||||
.append(".limit stack 10\n")
|
.append(".limit stack 10\n")
|
||||||
.append(".limit locals ").append(varMap.size() + 1).append("\n");
|
.append(".limit locals ").append(this.varMap.size() + 1).append("\n");
|
||||||
|
|
||||||
generateNode(ast.getRoot().getChildren().get(3).getChildren().get(11), jasmin, varMap, 0); // Skip crap
|
// Needs to be skipped to not trigger generation for IDENTIFIER: args or IDENTIFIER: ClassName
|
||||||
|
this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11));
|
||||||
|
|
||||||
jasmin.append("return\n")
|
this.jasmin.append("return\n")
|
||||||
.append(".end method\n");
|
.append(".end method\n");
|
||||||
|
|
||||||
Logger.log("Generated Jasmin Main.\n");
|
log("Generated Jasmin Main.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateNode(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void generateNode(ASTNode node) {
|
||||||
if (nodeMap.containsKey(node.getName())) {
|
if (methodMap.containsKey(node.getName())) {
|
||||||
try {
|
try {
|
||||||
nodeMap.get(node.getName()).invoke(null, node, jasmin, varMap, labelCounter);
|
methodMap.get(node.getName()).invoke(this, node);
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
node.getChildren().forEach(child -> generateNode(child, jasmin, varMap, labelCounter));
|
node.getChildren().forEach(this::generateNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: boolean expressions for conditions
|
// TODO: boolean expressions for conditions
|
||||||
// ifeq - if value is 0
|
// ifeq - if value is 0
|
||||||
// ifne - if value is not 0
|
// ifne - if value is not 0
|
||||||
private static void cond(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void cond(ASTNode node) {
|
||||||
|
this.labelCounter++;
|
||||||
|
|
||||||
// Condition
|
// Condition
|
||||||
generateNode(node.getChildren().get(0), jasmin, varMap, labelCounter);
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
// Jump
|
// Jump
|
||||||
jasmin.append("ifeq LabelFalse").append(labelCounter).append("\n")
|
this.jasmin.append("ifeq LabelFalse").append(this.labelCounter).append("\n")
|
||||||
.append("ifne LabelTrue").append(labelCounter).append("\n");
|
.append("ifne LabelTrue").append(this.labelCounter).append("\n");
|
||||||
|
|
||||||
// If branch
|
// If branch
|
||||||
jasmin.append("LabelTrue").append(labelCounter).append(":\n");
|
this.jasmin.append("LabelTrue").append(this.labelCounter).append(":\n");
|
||||||
generateNode(node.getChildren().get(1), jasmin, varMap, labelCounter + 1);
|
this.generateNode(node.getChildren().get(1));
|
||||||
jasmin.append("goto LabelFinish").append(labelCounter).append("\n");
|
this.jasmin.append("goto LabelFinish").append(this.labelCounter).append("\n");
|
||||||
|
|
||||||
// Else branch
|
// Else branch
|
||||||
jasmin.append("LabelFalse").append(labelCounter).append(":\n");
|
this.jasmin.append("LabelFalse").append(this.labelCounter).append(":\n");
|
||||||
if (node.getChildren().size() == 3) {
|
if (node.getChildren().size() == 3) {
|
||||||
// Else exists
|
// Else exists
|
||||||
|
|
||||||
generateNode(node.getChildren().get(2), jasmin, varMap, labelCounter + 1);
|
this.generateNode(node.getChildren().get(2));
|
||||||
}
|
}
|
||||||
jasmin.append("goto LabelFinish").append(labelCounter).append("\n"); // Optional
|
this.jasmin.append("goto LabelFinish").append(this.labelCounter).append("\n"); // Optional
|
||||||
|
|
||||||
jasmin.append("LabelFinish").append(labelCounter).append(":\n");
|
this.jasmin.append("LabelFinish").append(this.labelCounter).append(":\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assign(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void assign(ASTNode node) {
|
||||||
generateNode(node.getChildren().get(0), jasmin, varMap, labelCounter);
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
jasmin.append("istore ") //!: Type dependant
|
log("assign(): " + node.getName() + ": " + node.getValue() + " => istore");
|
||||||
.append(varMap.get(node.getValue()))
|
|
||||||
|
this.jasmin.append("istore ") //!: Type dependant
|
||||||
|
.append(this.varMap.get(node.getValue()))
|
||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void expr(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void expr(ASTNode node) {
|
||||||
String inst = "";
|
String inst = "";
|
||||||
|
|
||||||
if (node.getChildren().size() == 1) {
|
if (node.getChildren().size() == 1) {
|
||||||
// Unary operator
|
// Unary operator
|
||||||
|
|
||||||
generateNode(node.getChildren().get(0), jasmin, varMap, labelCounter);
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
inst = switch (node.getValue()) { //!: Type dependant
|
inst = switch (node.getValue()) { //!: Type dependant
|
||||||
case "ADD" -> "";
|
case "ADD" -> "";
|
||||||
@ -183,8 +204,8 @@ public final class CodeGenerator {
|
|||||||
} else if (node.getChildren().size() == 2) {
|
} else if (node.getChildren().size() == 2) {
|
||||||
// Binary operator
|
// Binary operator
|
||||||
|
|
||||||
generateNode(node.getChildren().get(0), jasmin, varMap, labelCounter);
|
this.generateNode(node.getChildren().get(0));
|
||||||
generateNode(node.getChildren().get(1), jasmin, varMap, labelCounter);
|
this.generateNode(node.getChildren().get(1));
|
||||||
|
|
||||||
inst = switch (node.getValue()) { //!: Type dependant
|
inst = switch (node.getValue()) { //!: Type dependant
|
||||||
case "ADD" -> "iadd"; // Integer
|
case "ADD" -> "iadd"; // Integer
|
||||||
@ -196,31 +217,36 @@ public final class CodeGenerator {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
jasmin.append(inst)
|
log("expr(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
||||||
|
|
||||||
|
this.jasmin.append(inst)
|
||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leafs
|
// Leafs
|
||||||
|
|
||||||
private static void literal(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void literal(ASTNode node) {
|
||||||
jasmin.append("bipush ") //!: Type dependant
|
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||||
|
|
||||||
|
// bipush only pushes 1 byte as int
|
||||||
|
this.jasmin.append("ldc ") //!: Type dependant
|
||||||
.append(node.getValue())
|
.append(node.getValue())
|
||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void identifier(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void identifier(ASTNode node) {
|
||||||
Logger.log(node.getName() + ": " + node.getValue() + " => iload");
|
log("identifier(): " + node.getName() + ": " + node.getValue() + " => iload");
|
||||||
|
|
||||||
jasmin.append("iload ") //!: Type dependent
|
this.jasmin.append("iload ") //!: Type dependent
|
||||||
.append(varMap.get(node.getValue()))
|
.append(this.varMap.get(node.getValue()))
|
||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void println(ASTNode node, StringBuilder jasmin, Map<String, Integer> varMap, int labelCounter) {
|
private void println(ASTNode node) {
|
||||||
jasmin.append("getstatic java/lang/System/out Ljava/io/PrintStream;\n"); // Push System.out to stack
|
this.jasmin.append("getstatic java/lang/System/out Ljava/io/PrintStream;\n"); // Push System.out to stack
|
||||||
|
|
||||||
generateNode(node.getChildren().get(1).getChildren().get(1), jasmin, varMap, labelCounter);
|
this.generateNode(node.getChildren().get(1).getChildren().get(1));
|
||||||
|
|
||||||
jasmin.append("invokevirtual java/io/PrintStream/println(I)V\n"); //!: Type dependent
|
this.jasmin.append("invokevirtual java/io/PrintStream/println(I)V\n"); //!: Type dependent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,6 +132,7 @@ class CodeGeneratorTest {
|
|||||||
Arguments.of("6 % 8", 6),
|
Arguments.of("6 % 8", 6),
|
||||||
Arguments.of("6 % 2", 0), // 15
|
Arguments.of("6 % 2", 0), // 15
|
||||||
Arguments.of("6 % 4", 2),
|
Arguments.of("6 % 4", 2),
|
||||||
|
Arguments.of("123456789 - 123456780", 9),
|
||||||
Arguments.of("(6 % 4 + 2) * 6 / 3 * (2 + 4) - 1", 47),
|
Arguments.of("(6 % 4 + 2) * 6 / 3 * (2 + 4) - 1", 47),
|
||||||
Arguments.of("5 - 2 * 3", -1),
|
Arguments.of("5 - 2 * 3", -1),
|
||||||
Arguments.of("5 - 2 * 3 + 6 / 2 * 3 + 1 / 1 - 5 * 8 / 2 * 1 + 7 / 6", -10), // 20
|
Arguments.of("5 - 2 * 3 + 6 / 2 * 3 + 1 / 1 - 5 * 8 / 2 * 1 + 7 / 6", -10), // 20
|
||||||
@ -193,7 +194,8 @@ class CodeGeneratorTest {
|
|||||||
|
|
||||||
final AST tree = lexParseProgram(program);
|
final AST tree = lexParseProgram(program);
|
||||||
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
|
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
|
||||||
final StringBuilder srcProg = CodeGenerator.generateCode(tree, "TestOutput", nodeTable);
|
final CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
||||||
|
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
||||||
@ -207,7 +209,8 @@ class CodeGeneratorTest {
|
|||||||
|
|
||||||
final AST tree = lexParseProgram(program);
|
final AST tree = lexParseProgram(program);
|
||||||
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
|
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
|
||||||
final StringBuilder srcProg = CodeGenerator.generateCode(tree, "TestOutput", nodeTable);
|
final CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
||||||
|
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
||||||
@ -221,7 +224,8 @@ class CodeGeneratorTest {
|
|||||||
|
|
||||||
final AST tree = lexParseProgram(program);
|
final AST tree = lexParseProgram(program);
|
||||||
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
|
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
|
||||||
final StringBuilder srcProg = CodeGenerator.generateCode(tree, "TestOutput", nodeTable);
|
final CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
||||||
|
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(Boolean.parseBoolean(executeCompiledProgram())).isEqualTo(result);
|
assertThat(Boolean.parseBoolean(executeCompiledProgram())).isEqualTo(result);
|
||||||
|
|||||||
Reference in New Issue
Block a user