determine stack size simply by trying
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
package codegen;
|
||||
|
||||
import codegen.analysis.StackSizeAnalyzer;
|
||||
import parser.ast.AST;
|
||||
import parser.ast.ASTNode;
|
||||
|
||||
@ -93,7 +94,6 @@ public final class CodeGenerator {
|
||||
}
|
||||
|
||||
private void generateHeader(String source) {
|
||||
System.out.println(" - Generating Jasmin Assembler...");
|
||||
final String clazz = this.tree.getRoot().getChildren().get(1).getValue();
|
||||
|
||||
this.jasmin.append(".bytecode 49.0\n") // 55.0 has stricter verification => stackmap frames missing
|
||||
@ -118,6 +118,7 @@ public final class CodeGenerator {
|
||||
}
|
||||
|
||||
public StringBuilder generateCode(String source) {
|
||||
System.out.println(" - Generating Jasmin assembler...");
|
||||
if (!this.tree.getRoot().hasChildren()) {
|
||||
throw new CodeGenerationException("Empty File can't be compiled");
|
||||
}
|
||||
@ -132,12 +133,13 @@ public final class CodeGenerator {
|
||||
return this.jasmin;
|
||||
}
|
||||
|
||||
// TODO: Stack size
|
||||
private void generateMain() {
|
||||
this.jasmin.append(".method public static main([Ljava/lang/String;)V\n")
|
||||
.append("\t.limit stack 10\n")
|
||||
.append("\t.limit stack ").append(StackSizeAnalyzer.model(this.tree)).append("\n")
|
||||
.append("\t.limit locals ").append(this.varMap.size() + 1).append("\n");
|
||||
|
||||
log("\nGenerating main method code");
|
||||
|
||||
// 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));
|
||||
|
||||
@ -207,7 +209,7 @@ public final class CodeGenerator {
|
||||
this.jasmin.append("LOOPend").append(currentLabel).append(":\n");
|
||||
}
|
||||
|
||||
private void assign(ASTNode node) {
|
||||
private void assign(ASTNode node) { //! Stack - 1
|
||||
this.generateNode(node.getChildren().get(0));
|
||||
|
||||
final String type = this.nodeTypeMap.get(node.getChildren().get(0));
|
||||
@ -236,17 +238,17 @@ public final class CodeGenerator {
|
||||
private void intExpr(ASTNode node) {
|
||||
String inst = "";
|
||||
|
||||
if (node.getChildren().size() == 1) {
|
||||
if (node.getChildren().size() == 1) { //! Stack + 0
|
||||
// Unary operator
|
||||
|
||||
this.generateNode(node.getChildren().get(0));
|
||||
|
||||
inst = switch (node.getValue()) {
|
||||
case "ADD" -> "";
|
||||
case "SUB" -> "ldc -1\n\t\timul";
|
||||
case "SUB" -> "ineg";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + node.getValue());
|
||||
};
|
||||
} else if (node.getChildren().size() == 2) {
|
||||
} else if (node.getChildren().size() == 2) { //! Stack - 1
|
||||
// Binary operator
|
||||
|
||||
this.generateNode(node.getChildren().get(0));
|
||||
@ -272,7 +274,7 @@ public final class CodeGenerator {
|
||||
private void boolExpr(ASTNode node) {
|
||||
String inst = "";
|
||||
|
||||
if (node.getChildren().size() == 1) {
|
||||
if (node.getChildren().size() == 1) { //! Stack + 1
|
||||
// Unary operator
|
||||
|
||||
if (!"NOT".equals(node.getValue())) {
|
||||
@ -282,11 +284,9 @@ public final class CodeGenerator {
|
||||
|
||||
this.generateNode(node.getChildren().get(0));
|
||||
|
||||
// 1 -> 0, 0 -> 1?
|
||||
// inst = "ldc 1\n\t\tisub\n\t\tdup\n\t\timul"; // Subtract 1 and square for now
|
||||
inst = "ldc 1\n\t\tixor"; // 0 ^1 = 1, 1 ^1 = 0
|
||||
|
||||
} else if (node.getChildren().size() == 2) {
|
||||
} else if (node.getChildren().size() == 2) { //! Stack - 1
|
||||
// Binary operator
|
||||
|
||||
final int currentLabel = this.labelCounter;
|
||||
@ -329,7 +329,7 @@ public final class CodeGenerator {
|
||||
|
||||
// Leafs
|
||||
|
||||
private void intLiteral(ASTNode node) {
|
||||
private void intLiteral(ASTNode node) { //! Stack + 1
|
||||
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||
|
||||
// bipush only pushes 1 byte as int
|
||||
@ -338,7 +338,7 @@ public final class CodeGenerator {
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
private void stringLiteral(ASTNode node) {
|
||||
private void stringLiteral(ASTNode node) { //! Stack + 1
|
||||
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||
|
||||
// bipush only pushes 1 byte as int
|
||||
@ -347,7 +347,7 @@ public final class CodeGenerator {
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
private void boolLiteral(ASTNode node) {
|
||||
private void boolLiteral(ASTNode node) { //! Stack + 1
|
||||
log("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||
|
||||
final String val = "true".equals(node.getValue()) ? "1" : "0";
|
||||
@ -357,7 +357,7 @@ public final class CodeGenerator {
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
private void identifier(ASTNode node) {
|
||||
private void identifier(ASTNode node) { //! Stack + 1
|
||||
final String type = this.nodeTypeMap.get(node);
|
||||
final String inst = switch (type) {
|
||||
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload ";
|
||||
@ -373,7 +373,7 @@ public final class CodeGenerator {
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
private void println(ASTNode node) {
|
||||
private void println(ASTNode node) { //! Stack + 1
|
||||
this.jasmin.append("\t\tgetstatic java/lang/System/out Ljava/io/PrintStream;\n"); // Push System.out to stack
|
||||
|
||||
final ASTNode expr = node.getChildren().get(1).getChildren().get(1);
|
||||
|
46
src/main/java/codegen/analysis/StackModel.java
Normal file
46
src/main/java/codegen/analysis/StackModel.java
Normal file
@ -0,0 +1,46 @@
|
||||
package codegen.analysis;
|
||||
|
||||
import parser.ast.ASTNode;
|
||||
import util.Logger;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class StackModel {
|
||||
|
||||
private final Deque<ASTNode> stack;
|
||||
private int max;
|
||||
|
||||
public StackModel() {
|
||||
this.stack = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
public void push(ASTNode root) {
|
||||
Logger.log("PUSH " + root.getName() + ": " + root.getValue());
|
||||
this.stack.push(root);
|
||||
this.updateMax();
|
||||
}
|
||||
|
||||
public ASTNode pop() {
|
||||
if (this.stack.isEmpty()) {
|
||||
throw new IllegalStateException("Can't pop empty stack");
|
||||
}
|
||||
|
||||
Logger.log("POP " + this.stack.peek().getName() + ": " + this.stack.peek().getValue());
|
||||
return this.stack.pop();
|
||||
}
|
||||
|
||||
public ASTNode peek() {
|
||||
return this.stack.peek();
|
||||
}
|
||||
|
||||
private void updateMax() {
|
||||
if (this.stack.size() > this.max) {
|
||||
this.max = this.stack.size();
|
||||
}
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
return this.max;
|
||||
}
|
||||
}
|
88
src/main/java/codegen/analysis/StackSizeAnalyzer.java
Normal file
88
src/main/java/codegen/analysis/StackSizeAnalyzer.java
Normal file
@ -0,0 +1,88 @@
|
||||
package codegen.analysis;
|
||||
|
||||
import parser.ast.AST;
|
||||
import parser.ast.ASTNode;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static util.Logger.log;
|
||||
|
||||
public final class StackSizeAnalyzer {
|
||||
|
||||
private static final Set<String> mod;
|
||||
private static final Set<String> bin;
|
||||
|
||||
static {
|
||||
mod = Set.of("assignment", "expr", "INTEGER_LIT", "BOOLEAN_LIT", "STRING_LIT", "IDENTIFIER", "print");
|
||||
bin = Set.of("AND", "OR", "ADD", "SUB", "MUL", "DIV", "MOD", "LESS", "LESS_EQUAL", "GREATER", "GREATER_EQUAL", "EQUAL", "NOT_EQUAL");
|
||||
}
|
||||
|
||||
private StackSizeAnalyzer() {}
|
||||
|
||||
public static int model(AST tree) {
|
||||
log("\nDetermining required stack depth:");
|
||||
|
||||
final StackModel stack = new StackModel();
|
||||
model(tree.getRoot().getChildren().get(3).getChildren().get(11), stack);
|
||||
|
||||
return stack.getMax();
|
||||
}
|
||||
|
||||
private static void model(ASTNode root, StackModel stack) {
|
||||
if (mod.contains(root.getName())) {
|
||||
switch (root.getName()) {
|
||||
case "assignment" -> assignment(root, stack);
|
||||
case "INTEGER_LIT", "BOOLEAN_LIT", "STRING_LIT", "IDENTIFIER" -> literal(root, stack);
|
||||
case "expr" -> expr(root, stack);
|
||||
case "print" -> println(root, stack);
|
||||
default -> throw new IllegalStateException("Unexpected value: " + root.getName());
|
||||
}
|
||||
} else {
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
model(child, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void literal(ASTNode root, StackModel stack) {
|
||||
log("literal():");
|
||||
stack.push(root);
|
||||
}
|
||||
|
||||
private static void assignment(ASTNode root, StackModel stack) {
|
||||
model(root.getChildren().get(0), stack);
|
||||
|
||||
log("assignment():");
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
private static void println(ASTNode root, StackModel stack) {
|
||||
stack.push(root); // Getstatic
|
||||
|
||||
model(root.getChildren().get(1).getChildren().get(1), stack);
|
||||
|
||||
log("println():");
|
||||
stack.pop(); // Objectref
|
||||
stack.pop(); // Argument
|
||||
}
|
||||
|
||||
private static void expr(ASTNode root, StackModel stack) {
|
||||
if (root.getChildren().size() == 2 && bin.contains(root.getValue())) {
|
||||
model(root.getChildren().get(0), stack);
|
||||
model(root.getChildren().get(1), stack);
|
||||
|
||||
log("expr():");
|
||||
stack.pop(); // Argument
|
||||
stack.pop(); // Argument
|
||||
stack.push(root); // Result
|
||||
} else if (root.getChildren().size() == 1 && "NOT".equals(root.getValue())) {
|
||||
model(root.getChildren().get(0), stack);
|
||||
|
||||
log("expr():");
|
||||
stack.push(root); // 1 for xor
|
||||
stack.pop(); // xor
|
||||
stack.pop(); // xor
|
||||
stack.push(root); // result
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user