renamings
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
import codegen.CodeGenerator;
|
import codegen.sourcegraph.SourceGraph;
|
||||||
|
import codegen.sourcegraph.SourceGraphGenerator;
|
||||||
import lexer.StupsLexer;
|
import lexer.StupsLexer;
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
import org.antlr.v4.runtime.Lexer;
|
import org.antlr.v4.runtime.Lexer;
|
||||||
@ -78,11 +79,11 @@ public final class StupsCompiler {
|
|||||||
|
|
||||||
// Codegeneration + Output
|
// Codegeneration + Output
|
||||||
final String outputName = filename.replaceFirst("stups", "j");
|
final String outputName = filename.replaceFirst("stups", "j");
|
||||||
final CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, filename);
|
||||||
final StringBuilder jasmin = gen.generateCode(filename);
|
final SourceGraph graph = gen.generateCode();
|
||||||
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, graph.toString());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.out.println("Datei konnte nicht geschrieben werden.");
|
System.out.println("Datei konnte nicht geschrieben werden.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,39 +0,0 @@
|
|||||||
package codegen.analysis.flowgraph;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class FlowGraph {
|
|
||||||
|
|
||||||
private final FlowGraphHead head;
|
|
||||||
private final List<FlowGraphBlock> blocks;
|
|
||||||
private final FlowGraphTail tail;
|
|
||||||
|
|
||||||
public FlowGraph(FlowGraphHead head, List<FlowGraphBlock> blocks, FlowGraphTail tail) {
|
|
||||||
this.head = head;
|
|
||||||
this.blocks = blocks;
|
|
||||||
this.tail = tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void print() {
|
|
||||||
final String blocksString = this.blocks.stream()
|
|
||||||
.map(FlowGraphBlock::toString)
|
|
||||||
.map(string -> string + "-".repeat(50))
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
|
|
||||||
System.out.println(this.head + "-".repeat(100)
|
|
||||||
+ blocksString + "-".repeat(100)
|
|
||||||
+ this.tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
final String blocksString = this.blocks.stream()
|
|
||||||
.map(FlowGraphBlock::toString)
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
|
|
||||||
return this.head
|
|
||||||
+ blocksString
|
|
||||||
+ this.tail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
package codegen.analysis.flowgraph;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class FlowGraphBlock {
|
|
||||||
|
|
||||||
private final String label;
|
|
||||||
private final List<FlowGraphLine> lines;
|
|
||||||
|
|
||||||
public FlowGraphBlock(String label, List<FlowGraphLine> lines) {
|
|
||||||
this.label = label;
|
|
||||||
this.lines = lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
final String linesString = this.lines.stream()
|
|
||||||
.map(FlowGraphLine::toString)
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
|
|
||||||
return this.label + ":\n"
|
|
||||||
+ linesString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package codegen.analysis.flowgraph;
|
|
||||||
|
|
||||||
public class FlowGraphTail {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "\t\treturn\n"
|
|
||||||
+ ".end method";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
src/main/java/codegen/sourcegraph/SourceGraph.java
Normal file
63
src/main/java/codegen/sourcegraph/SourceGraph.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SourceGraph {
|
||||||
|
|
||||||
|
private final SourceGraphHead head;
|
||||||
|
private final List<SourceGraphBlock> blocks;
|
||||||
|
private final SourceGraphTail tail;
|
||||||
|
|
||||||
|
public SourceGraph(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) {
|
||||||
|
this.head = new SourceGraphHead(bytecodeVersion, source, clazz, stackSize, localCount);
|
||||||
|
this.blocks = new ArrayList<>();
|
||||||
|
this.tail = new SourceGraphTail();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLabel(String label) {
|
||||||
|
if (this.blocks.get(this.blocks.size() - 1).isEmpty()) {
|
||||||
|
// Replace empty blocks, we don't need them
|
||||||
|
|
||||||
|
this.blocks.set(this.blocks.size() - 1, new SourceGraphBlock(label));
|
||||||
|
} else {
|
||||||
|
this.blocks.add(new SourceGraphBlock(label));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInst(String instruction, String... args) {
|
||||||
|
if (this.blocks.isEmpty()) {
|
||||||
|
this.blocks.add(new SourceGraphBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blocks.get(this.blocks.size() - 1).addLine(instruction, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addJump(String instruction, String label) {
|
||||||
|
this.addInst(instruction, label);
|
||||||
|
this.blocks.add(new SourceGraphBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String print() {
|
||||||
|
final String blocksString = this.blocks.stream()
|
||||||
|
.map(SourceGraphBlock::toString)
|
||||||
|
.map(string -> string + "-".repeat(50) + "\n")
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
return this.head + "-".repeat(100) + "\n"
|
||||||
|
+ "-".repeat(50) + "\n" + blocksString + "-".repeat(100) + "\n"
|
||||||
|
+ this.tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final String blocksString = this.blocks.stream()
|
||||||
|
.map(SourceGraphBlock::toString)
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
return this.head
|
||||||
|
+ blocksString
|
||||||
|
+ this.tail;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/main/java/codegen/sourcegraph/SourceGraphBlock.java
Normal file
44
src/main/java/codegen/sourcegraph/SourceGraphBlock.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SourceGraphBlock {
|
||||||
|
|
||||||
|
private final String label;
|
||||||
|
private final List<SourceGraphInst> lines;
|
||||||
|
|
||||||
|
public SourceGraphBlock(String label) {
|
||||||
|
this.label = label;
|
||||||
|
this.lines = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceGraphBlock() {
|
||||||
|
this.label = "";
|
||||||
|
this.lines = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.label.isEmpty() && this.lines.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final String linesString = this.lines.stream()
|
||||||
|
.map(SourceGraphInst::toString)
|
||||||
|
.map(line -> line + "\n")
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
if (this.label.isBlank()) {
|
||||||
|
return linesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.label + ":\n"
|
||||||
|
+ linesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLine(String instruction, String... args) {
|
||||||
|
this.lines.add(new SourceGraphInst(instruction, args));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package codegen;
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
|
import codegen.CodeGenerationException;
|
||||||
import codegen.analysis.StackSizeAnalyzer;
|
import codegen.analysis.StackSizeAnalyzer;
|
||||||
import parser.ast.AST;
|
import parser.ast.AST;
|
||||||
import parser.ast.ASTNode;
|
import parser.ast.ASTNode;
|
||||||
@ -15,23 +16,23 @@ import java.util.Map;
|
|||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static util.Logger.log;
|
import static util.Logger.log;
|
||||||
|
|
||||||
public final class CodeGenerator {
|
public final class SourceGraphGenerator {
|
||||||
|
|
||||||
private static final Map<String, Method> methodMap;
|
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 = SourceGraphGenerator.class;
|
||||||
map = Map.ofEntries(
|
map = Map.ofEntries(
|
||||||
entry("cond", gen.getDeclaredMethod("cond", ASTNode.class)),
|
entry("cond", gen.getDeclaredMethod("cond", ASTNode.class)),
|
||||||
entry("loop", gen.getDeclaredMethod("loop", ASTNode.class)),
|
entry("loop", gen.getDeclaredMethod("loop", ASTNode.class)),
|
||||||
entry("assignment", gen.getDeclaredMethod("assign", ASTNode.class)),
|
entry("assignment", gen.getDeclaredMethod("assign", ASTNode.class)),
|
||||||
entry("expr", gen.getDeclaredMethod("expr", ASTNode.class)),
|
entry("expr", gen.getDeclaredMethod("expr", ASTNode.class)),
|
||||||
// Leafs
|
// Leafs
|
||||||
entry("INTEGER_LIT", gen.getDeclaredMethod("intLiteral", ASTNode.class)),
|
entry("INTEGER_LIT", gen.getDeclaredMethod("intStringLiteral", ASTNode.class)),
|
||||||
entry("BOOLEAN_LIT", gen.getDeclaredMethod("boolLiteral", ASTNode.class)),
|
entry("BOOLEAN_LIT", gen.getDeclaredMethod("boolLiteral", ASTNode.class)),
|
||||||
entry("STRING_LIT", gen.getDeclaredMethod("stringLiteral", ASTNode.class)),
|
entry("STRING_LIT", gen.getDeclaredMethod("intStringLiteral", ASTNode.class)),
|
||||||
entry("IDENTIFIER", gen.getDeclaredMethod("identifier", ASTNode.class)),
|
entry("IDENTIFIER", gen.getDeclaredMethod("identifier", ASTNode.class)),
|
||||||
entry("print", gen.getDeclaredMethod("println", ASTNode.class))
|
entry("print", gen.getDeclaredMethod("println", ASTNode.class))
|
||||||
);
|
);
|
||||||
@ -44,20 +45,34 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
private final Map<String, Integer> varMap;
|
private final Map<String, Integer> varMap;
|
||||||
private final Map<ASTNode, String> nodeTypeMap;
|
private final Map<ASTNode, String> nodeTypeMap;
|
||||||
private final StringBuilder jasmin;
|
private final String source;
|
||||||
private final AST tree;
|
private final AST tree;
|
||||||
|
private final SourceGraph graph;
|
||||||
|
|
||||||
private int labelCounter;
|
private int labelCounter;
|
||||||
|
|
||||||
private CodeGenerator(Map<String, Integer> varMap, AST tree, Map<ASTNode, String> nodeTypeMap) {
|
private SourceGraphGenerator(Map<String, Integer> varMap, AST tree, Map<ASTNode, String> nodeTypeMap, SourceGraph graph, String source) {
|
||||||
this.varMap = varMap;
|
this.varMap = varMap;
|
||||||
this.tree = tree;
|
this.tree = tree;
|
||||||
this.nodeTypeMap = nodeTypeMap;
|
this.nodeTypeMap = nodeTypeMap;
|
||||||
this.jasmin = new StringBuilder();
|
this.graph = graph;
|
||||||
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CodeGenerator fromAST(AST tree, Map<ASTNode, String> nodeTypeMap) {
|
public static SourceGraphGenerator fromAST(AST tree, Map<ASTNode, String> nodeTypeMap, String source) {
|
||||||
return new CodeGenerator(varMapFromAST(tree), tree, nodeTypeMap);
|
if (!tree.getRoot().hasChildren()) {
|
||||||
|
throw new CodeGenerationException("Empty File can't be compiled");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, Integer> varMap = varMapFromAST(tree);
|
||||||
|
|
||||||
|
final String bytecodeVersion = "49.0";
|
||||||
|
final String clazz = tree.getRoot().getChildren().get(1).getValue();
|
||||||
|
final int stackSize = StackSizeAnalyzer.runStackModel(tree);
|
||||||
|
final int localCount = varMap.size() + 1;
|
||||||
|
final SourceGraph graph = new SourceGraph(bytecodeVersion, source, clazz, stackSize, localCount);
|
||||||
|
|
||||||
|
return new SourceGraphGenerator(varMap, tree, nodeTypeMap, graph, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Integer> varMapFromAST(AST tree) {
|
private static Map<String, Integer> varMapFromAST(AST tree) {
|
||||||
@ -84,69 +99,16 @@ public final class CodeGenerator {
|
|||||||
return Collections.unmodifiableMap(varMap);
|
return Collections.unmodifiableMap(varMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String genComparisonInst(String inst, String labelPre, int currentLabel) {
|
public SourceGraph generateCode() {
|
||||||
return inst + " " + labelPre + "true" + currentLabel // If not equal jump to NEtrue
|
|
||||||
+ "\n\t\tldc 0" // If false load 0
|
|
||||||
+ "\n\t\tgoto " + labelPre + "end" + currentLabel // If false skip true branch
|
|
||||||
+ "\n" + labelPre + "true" + currentLabel + ":"
|
|
||||||
+ "\n\t\tldc 1" // if true load 1
|
|
||||||
+ "\n" + labelPre + "end" + currentLabel + ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateHeader(String source) {
|
|
||||||
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
|
|
||||||
.append(".source ").append(source).append("\n")
|
|
||||||
.append(".class public ").append(clazz).append("\n")
|
|
||||||
.append(".super java/lang/Object\n");
|
|
||||||
|
|
||||||
log("Generated Jasmin Header.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateConstructor() {
|
|
||||||
this.jasmin.append(".method public <init>()V\n")
|
|
||||||
.append("\t.limit stack 1\n")
|
|
||||||
.append("\t.limit locals 1\n")
|
|
||||||
.append("\t.line 1\n")
|
|
||||||
.append("\t\taload_0\n")
|
|
||||||
.append("\t\tinvokespecial java/lang/Object/<init>()V\n")
|
|
||||||
.append("\t\treturn\n")
|
|
||||||
.append(".end method\n\n");
|
|
||||||
|
|
||||||
log("Generated Jasmin Constructor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringBuilder generateCode(String source) {
|
|
||||||
System.out.println(" - Generating Jasmin assembler...");
|
System.out.println(" - Generating Jasmin assembler...");
|
||||||
if (!this.tree.getRoot().hasChildren()) {
|
|
||||||
throw new CodeGenerationException("Empty File can't be compiled");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.generateHeader(source);
|
|
||||||
this.generateConstructor();
|
|
||||||
this.generateMain();
|
|
||||||
|
|
||||||
log("Jasmin Assembler:\n" + "-".repeat(100) + "\n" + this.jasmin + "-".repeat(100));
|
|
||||||
System.out.println("Code-generation successful.");
|
|
||||||
|
|
||||||
return this.jasmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateMain() {
|
|
||||||
this.jasmin.append(".method public static main([Ljava/lang/String;)V\n")
|
|
||||||
.append("\t.limit stack ").append(StackSizeAnalyzer.runStackModel(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));
|
this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11));
|
||||||
|
|
||||||
this.jasmin.append("\t\treturn\n")
|
log("\n\nJasmin Assembler from FlowGraph:\n" + "-".repeat(100) + "\n" + this.graph + "-".repeat(100));
|
||||||
.append(".end method\n");
|
log("\n\nFlowGraph print:\n" + "-".repeat(100) + "\n" + this.graph.print() + "-".repeat(100));
|
||||||
|
System.out.println("Code-generation successful.");
|
||||||
|
|
||||||
log("Generated Jasmin Main.\n");
|
return this.graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateNode(ASTNode node) {
|
private void generateNode(ASTNode node) {
|
||||||
@ -171,14 +133,14 @@ public final class CodeGenerator {
|
|||||||
this.generateNode(node.getChildren().get(0));
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
// Jump
|
// Jump
|
||||||
this.jasmin.append("\t\tifeq IFfalse").append(currentLabel).append("\n"); // ifeq: == 0 => false
|
this.graph.addJump("ifeq", "IFfalse" + currentLabel);
|
||||||
|
|
||||||
// IFtrue branch
|
// IFtrue branch
|
||||||
this.generateNode(node.getChildren().get(1));
|
this.generateNode(node.getChildren().get(1));
|
||||||
this.jasmin.append("\t\tgoto IFend").append(currentLabel).append("\n");
|
this.graph.addJump("goto", "IFend" + currentLabel);
|
||||||
|
|
||||||
// IFfalse branch
|
// IFfalse branch
|
||||||
this.jasmin.append("IFfalse").append(currentLabel).append(":\n");
|
this.graph.addLabel("IFfalse" + currentLabel);
|
||||||
if (node.getChildren().size() == 3) {
|
if (node.getChildren().size() == 3) {
|
||||||
// Else exists
|
// Else exists
|
||||||
|
|
||||||
@ -186,27 +148,27 @@ public final class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IFend branch
|
// IFend branch
|
||||||
this.jasmin.append("IFend").append(currentLabel).append(":\n");
|
this.graph.addLabel("IFend" + currentLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loop(ASTNode node) {
|
private void loop(ASTNode node) {
|
||||||
final int currentLabel = this.labelCounter;
|
final int currentLabel = this.labelCounter;
|
||||||
this.labelCounter++;
|
this.labelCounter++;
|
||||||
|
|
||||||
this.jasmin.append("LOOPstart").append(currentLabel).append(":\n");
|
this.graph.addLabel("LOOPstart" + currentLabel);
|
||||||
|
|
||||||
// Condition
|
// Condition
|
||||||
this.generateNode(node.getChildren().get(0).getChildren().get(1));
|
this.generateNode(node.getChildren().get(0).getChildren().get(1));
|
||||||
|
|
||||||
// Jump
|
// Jump
|
||||||
this.jasmin.append("\t\tifeq LOOPend").append(currentLabel).append("\n"); // ifeq: == 0 => Loop stopped
|
this.graph.addJump("ifeq", "LOOPend" + currentLabel);
|
||||||
|
|
||||||
// Loop body
|
// Loop body
|
||||||
this.generateNode(node.getChildren().get(1));
|
this.generateNode(node.getChildren().get(1));
|
||||||
this.jasmin.append("\t\tgoto LOOPstart").append(currentLabel).append("\n"); // Jump to Loop start
|
this.graph.addJump("goto", "LOOPstart" + currentLabel);
|
||||||
|
|
||||||
// Loop end
|
// Loop end
|
||||||
this.jasmin.append("LOOPend").append(currentLabel).append(":\n");
|
this.graph.addLabel("LOOPend" + currentLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assign(ASTNode node) { //! Stack - 1
|
private void assign(ASTNode node) { //! Stack - 1
|
||||||
@ -214,17 +176,14 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
final String type = this.nodeTypeMap.get(node.getChildren().get(0));
|
final String type = this.nodeTypeMap.get(node.getChildren().get(0));
|
||||||
final String inst = switch (type) {
|
final String inst = switch (type) {
|
||||||
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "istore ";
|
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "istore";
|
||||||
case "STRING_TYPE" -> "astore ";
|
case "STRING_TYPE" -> "astore";
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + type);
|
default -> throw new IllegalStateException("Unexpected value: " + type);
|
||||||
};
|
};
|
||||||
|
|
||||||
log("assign(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
log("assign(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
||||||
|
|
||||||
this.jasmin.append("\t\t")
|
this.graph.addInst(inst, this.varMap.get(node.getValue()).toString());
|
||||||
.append(inst)
|
|
||||||
.append(this.varMap.get(node.getValue()))
|
|
||||||
.append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expr(ASTNode node) {
|
private void expr(ASTNode node) {
|
||||||
@ -266,14 +225,10 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
log("intExpr(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
log("intExpr(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
||||||
|
|
||||||
this.jasmin.append("\t\t")
|
this.graph.addInst(inst);
|
||||||
.append(inst)
|
|
||||||
.append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void boolExpr(ASTNode node) {
|
private void boolExpr(ASTNode node) {
|
||||||
String inst = "";
|
|
||||||
|
|
||||||
if (node.getChildren().size() == 1) { //! Stack + 1
|
if (node.getChildren().size() == 1) { //! Stack + 1
|
||||||
// Unary operator
|
// Unary operator
|
||||||
|
|
||||||
@ -284,7 +239,9 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
this.generateNode(node.getChildren().get(0));
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
inst = "ldc 1\n\t\tixor"; // 0 ^1 = 1, 1 ^1 = 0
|
// 0 ^1 = 1, 1 ^1 = 0
|
||||||
|
this.graph.addInst("ldc", "1");
|
||||||
|
this.graph.addInst("ixor");
|
||||||
|
|
||||||
} else if (node.getChildren().size() == 2) { //! Stack - 1
|
} else if (node.getChildren().size() == 2) { //! Stack - 1
|
||||||
// Binary operator
|
// Binary operator
|
||||||
@ -307,44 +264,36 @@ public final class CodeGenerator {
|
|||||||
default -> throw new IllegalStateException("Unexpected value: " + type);
|
default -> throw new IllegalStateException("Unexpected value: " + type);
|
||||||
};
|
};
|
||||||
|
|
||||||
inst = switch (node.getValue()) {
|
switch (node.getValue()) {
|
||||||
case "AND" -> "iand"; // Boolean
|
case "AND" -> this.graph.addInst("iand"); // Boolean
|
||||||
case "OR" -> "ior";
|
case "OR" -> this.graph.addInst("ior");
|
||||||
case "EQUAL" -> genComparisonInst(cmpeq, "EQ", currentLabel);
|
case "EQUAL" -> this.genComparisonInst(cmpeq, "EQ", currentLabel);
|
||||||
case "NOT_EQUAL" -> genComparisonInst(cmpne, "NE", currentLabel);
|
case "NOT_EQUAL" -> this.genComparisonInst(cmpne, "NE", currentLabel);
|
||||||
case "LESS" -> genComparisonInst("if_icmplt", "LT", currentLabel);
|
case "LESS" -> this.genComparisonInst("if_icmplt", "LT", currentLabel);
|
||||||
case "LESS_EQUAL" -> genComparisonInst("if_icmple", "LE", currentLabel);
|
case "LESS_EQUAL" -> this.genComparisonInst("if_icmple", "LE", currentLabel);
|
||||||
case "GREATER" -> genComparisonInst("if_icmpgt", "GT", currentLabel);
|
case "GREATER" -> this.genComparisonInst("if_icmpgt", "GT", currentLabel);
|
||||||
case "GREATER_EQUAL" -> genComparisonInst("if_icmpge", "GE", currentLabel);
|
case "GREATER_EQUAL" -> this.genComparisonInst("if_icmpge", "GE", currentLabel);
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + node.getValue());
|
default -> throw new IllegalStateException("Unexpected value: " + node.getValue());
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log("boolExpr(): " + node.getName() + ": " + node.getValue() + " => \n\t\t" + inst);
|
private void genComparisonInst(String cmpInst, String labelPre, int currentLabel) {
|
||||||
|
this.graph.addJump(cmpInst, labelPre + "true" + currentLabel); // If not equal jump to NEtrue
|
||||||
this.jasmin.append("\t\t")
|
this.graph.addInst("ldc", "0"); // If false load 0
|
||||||
.append(inst)
|
this.graph.addJump("goto", labelPre + "end" + currentLabel); // If false skip to true
|
||||||
.append("\n");
|
this.graph.addLabel(labelPre + "true" + currentLabel);
|
||||||
|
this.graph.addInst("ldc", "1"); // If true load 1
|
||||||
|
this.graph.addLabel(labelPre + "end" + currentLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leafs
|
// Leafs
|
||||||
|
|
||||||
private void intLiteral(ASTNode node) { //! Stack + 1
|
private void intStringLiteral(ASTNode node) { //! Stack + 1
|
||||||
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||||
|
|
||||||
// bipush only pushes 1 byte as int
|
// bipush only pushes 1 byte as int
|
||||||
this.jasmin.append("\t\tldc ")
|
this.graph.addInst("ldc", node.getValue());
|
||||||
.append(node.getValue())
|
|
||||||
.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stringLiteral(ASTNode node) { //! Stack + 1
|
|
||||||
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
|
||||||
|
|
||||||
// bipush only pushes 1 byte as int
|
|
||||||
this.jasmin.append("\t\tldc ")
|
|
||||||
.append(node.getValue())
|
|
||||||
.append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void boolLiteral(ASTNode node) { //! Stack + 1
|
private void boolLiteral(ASTNode node) { //! Stack + 1
|
||||||
@ -352,29 +301,24 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
final String val = "true".equals(node.getValue()) ? "1" : "0";
|
final String val = "true".equals(node.getValue()) ? "1" : "0";
|
||||||
|
|
||||||
this.jasmin.append("\t\tldc ")
|
this.graph.addInst("ldc", val);
|
||||||
.append(val)
|
|
||||||
.append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void identifier(ASTNode node) { //! Stack + 1
|
private void identifier(ASTNode node) { //! Stack + 1
|
||||||
final String type = this.nodeTypeMap.get(node);
|
final String type = this.nodeTypeMap.get(node);
|
||||||
final String inst = switch (type) {
|
final String inst = switch (type) {
|
||||||
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload ";
|
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload";
|
||||||
case "STRING_TYPE" -> "aload ";
|
case "STRING_TYPE" -> "aload";
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + type);
|
default -> throw new IllegalStateException("Unexpected value: " + type);
|
||||||
};
|
};
|
||||||
|
|
||||||
log("identifier(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
log("identifier(): " + node.getName() + ": " + node.getValue() + " => " + inst);
|
||||||
|
|
||||||
this.jasmin.append("\t\t")
|
this.graph.addInst(inst, this.varMap.get(node.getValue()).toString());
|
||||||
.append(inst)
|
|
||||||
.append(this.varMap.get(node.getValue()))
|
|
||||||
.append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void println(ASTNode node) { //! Stack + 1
|
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
|
this.graph.addInst("getstatic", "java/lang/System/out", "Ljava/io/PrintStream;");
|
||||||
|
|
||||||
final ASTNode expr = node.getChildren().get(1).getChildren().get(1);
|
final ASTNode expr = node.getChildren().get(1).getChildren().get(1);
|
||||||
final String type = switch (this.nodeTypeMap.get(expr)) {
|
final String type = switch (this.nodeTypeMap.get(expr)) {
|
||||||
@ -388,8 +332,6 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
log("println(): " + expr.getName() + ": " + expr.getValue() + " => " + type);
|
log("println(): " + expr.getName() + ": " + expr.getValue() + " => " + type);
|
||||||
|
|
||||||
this.jasmin.append("\t\tinvokevirtual java/io/PrintStream/println(")
|
this.graph.addInst("invokevirtual", "java/io/PrintStream/println(" + type + ")V");
|
||||||
.append(type)
|
|
||||||
.append(")V\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package codegen.analysis.flowgraph;
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
public class FlowGraphHead {
|
public class SourceGraphHead {
|
||||||
|
|
||||||
private final String bytecodeVersion;
|
private final String bytecodeVersion;
|
||||||
private final String source;
|
private final String source;
|
||||||
@ -8,7 +8,7 @@ public class FlowGraphHead {
|
|||||||
private final int stackSize;
|
private final int stackSize;
|
||||||
private final int localCount;
|
private final int localCount;
|
||||||
|
|
||||||
public FlowGraphHead(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) {
|
public SourceGraphHead(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) {
|
||||||
this.bytecodeVersion = bytecodeVersion;
|
this.bytecodeVersion = bytecodeVersion;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.clazz = clazz;
|
this.clazz = clazz;
|
||||||
@ -20,7 +20,7 @@ public class FlowGraphHead {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return ".bytecode " + this.bytecodeVersion + "\n"
|
return ".bytecode " + this.bytecodeVersion + "\n"
|
||||||
+ ".source " + this.source + "\n"
|
+ ".source " + this.source + "\n"
|
||||||
+ ".class public" + this.clazz + "\n"
|
+ ".class public " + this.clazz + "\n"
|
||||||
+ ".super java/lang/Object\n"
|
+ ".super java/lang/Object\n"
|
||||||
+ ".method public <init>()V\n"
|
+ ".method public <init>()V\n"
|
||||||
+ "\t.limit stack 1\n"
|
+ "\t.limit stack 1\n"
|
||||||
@ -1,11 +1,11 @@
|
|||||||
package codegen.analysis.flowgraph;
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
public class FlowGraphLine {
|
public class SourceGraphInst {
|
||||||
|
|
||||||
private final String instruction;
|
private final String instruction;
|
||||||
private final String[] args;
|
private final String[] args;
|
||||||
|
|
||||||
public FlowGraphLine(String instruction, String... args) {
|
public SourceGraphInst(String instruction, String... args) {
|
||||||
this.instruction = instruction;
|
this.instruction = instruction;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
10
src/main/java/codegen/sourcegraph/SourceGraphTail.java
Normal file
10
src/main/java/codegen/sourcegraph/SourceGraphTail.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
|
public class SourceGraphTail {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "\t\treturn\n"
|
||||||
|
+ ".end method\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package codegen;
|
package codegen;
|
||||||
|
|
||||||
|
import codegen.sourcegraph.SourceGraph;
|
||||||
|
import codegen.sourcegraph.SourceGraphGenerator;
|
||||||
import lexer.StupsLexer;
|
import lexer.StupsLexer;
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
import org.antlr.v4.runtime.Lexer;
|
import org.antlr.v4.runtime.Lexer;
|
||||||
@ -27,14 +29,14 @@ import java.util.stream.Stream;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
class CodeGeneratorTest {
|
class SourceGraphGeneratorTest {
|
||||||
|
|
||||||
private static StupsParser parser;
|
private static StupsParser parser;
|
||||||
private static Grammar stupsGrammar;
|
private static Grammar stupsGrammar;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void init() throws IOException, URISyntaxException {
|
static void init() throws IOException, URISyntaxException {
|
||||||
final Path path = Paths.get(CodeGeneratorTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI());
|
final Path path = Paths.get(SourceGraphGeneratorTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI());
|
||||||
final Grammar grammar = Grammar.fromFile(path);
|
final Grammar grammar = Grammar.fromFile(path);
|
||||||
parser = StupsParser.fromGrammar(grammar);
|
parser = StupsParser.fromGrammar(grammar);
|
||||||
stupsGrammar = grammar;
|
stupsGrammar = grammar;
|
||||||
@ -42,7 +44,7 @@ class CodeGeneratorTest {
|
|||||||
|
|
||||||
private static String readProgram(String prog) {
|
private static String readProgram(String prog) {
|
||||||
try {
|
try {
|
||||||
final Path progPath = Paths.get(CodeGeneratorTest.class.getClassLoader().getResource("examplePrograms/" + prog).toURI());
|
final Path progPath = Paths.get(SourceGraphGeneratorTest.class.getClassLoader().getResource("examplePrograms/" + prog).toURI());
|
||||||
return Files.readString(progPath);
|
return Files.readString(progPath);
|
||||||
} catch (URISyntaxException | IOException e) {
|
} catch (URISyntaxException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -240,8 +242,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 CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutpu");
|
||||||
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
final SourceGraph srcProg = gen.generateCode();
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
||||||
@ -255,8 +257,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 CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput");
|
||||||
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
final SourceGraph srcProg = gen.generateCode();
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result);
|
||||||
@ -270,8 +272,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 CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput");
|
||||||
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
final SourceGraph srcProg = gen.generateCode();
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(Boolean.parseBoolean(executeCompiledProgram())).isEqualTo(result);
|
assertThat(Boolean.parseBoolean(executeCompiledProgram())).isEqualTo(result);
|
||||||
@ -285,8 +287,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 CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput");
|
||||||
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
final SourceGraph srcProg = gen.generateCode();
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(executeCompiledProgram()).isEqualTo(result);
|
assertThat(executeCompiledProgram()).isEqualTo(result);
|
||||||
@ -300,8 +302,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 CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput");
|
||||||
final StringBuilder srcProg = gen.generateCode("TestOutput");
|
final SourceGraph srcProg = gen.generateCode();
|
||||||
|
|
||||||
compileJasmin(srcProg.toString());
|
compileJasmin(srcProg.toString());
|
||||||
assertThat(executeCompiledProgram()).isEqualTo(result);
|
assertThat(executeCompiledProgram()).isEqualTo(result);
|
||||||
@ -313,8 +315,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 CodeGenerator gen = CodeGenerator.fromAST(tree, nodeTable);
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> gen.generateCode("TestOutput")).isInstanceOf(CodeGenerationException.class);
|
assertThatThrownBy(() -> SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput"))
|
||||||
|
.isInstanceOf(CodeGenerationException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user