Kompilaatori kodutöö, vihje 2
package week10;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import week7.AktkAst;
import week7.ast.*;
import week9.AktkBinding;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.objectweb.asm.Opcodes.*;
public class AktkCompiler {
static void main(String[] args) throws IOException {
// lihtsam viis "käsurea parameetrite andmiseks":
//args = new String[] {"inputs/yks_pluss_yks.aktk"};
if (args.length != 1) {
throw new IllegalArgumentException("Sellele programmile tuleb anda parameetriks kompileeritava AKTK faili nimi");
}
Path sourceFile = Paths.get(args[0]);
if (!Files.isRegularFile(sourceFile)) {
throw new IllegalArgumentException("Ei leia faili nimega '" + sourceFile + "'");
}
String className = sourceFile.getFileName().toString().replace(".aktk", "");
Path classFile = sourceFile.toAbsolutePath().getParent().resolve(className + ".class");
createClassFile(sourceFile, className, classFile);
}
private static void createClassFile(Path sourceFile, String className, Path classFile) throws IOException {
// loen faili sisu muutujasse
String source = Files.readString(sourceFile);
// parsin ja moodustan AST'i
Statement ast = AktkAst.createAst(source);
// seon muutujad
AktkBinding.bind(ast);
// kompileerin
byte[] bytes = createClass(ast, className);
Files.write(classFile, bytes);
}
public static byte[] createClass(Statement statement, String className) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// Klassi attribuudid
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", null);
cw.visitSource(null, null);
// main meetod
MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC + ACC_STATIC, // modifikaatorid
"main", // meetodi nimi
"([Ljava/lang/String;)V", // meetodi kirjeldaja
null, // geneerikute info
new String[] { "java/io/IOException" });
mv.visitCode();
// terve AKTK programm tuleb kompileerida main meetodi sisse
new AktkCompiler(mv).compileStatement(statement);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// klassi lõpetamine
cw.visitEnd();
// klassi baidijada genereerimine
return cw.toByteArray();
}
private final MethodVisitor mv;
private final Map<VariableBinding, Integer> variableIndices = new HashMap<>();
public AktkCompiler(MethodVisitor mv) {
this.mv = mv;
}
private int getVariableIndex(VariableBinding binding) {
// Kasuta teadaolevat indeksit, kui see on olemas,
// või leia järgmine vaba indeks ja salvesta see.
// Esimene muutuja saab indeksi 1, sest indeksil 0 on main meetodi parameeter (String args[]).
return variableIndices.computeIfAbsent(binding, ignoreBinding -> variableIndices.size() + 1);
}
private void compileStatement(Statement statement) {
switch (statement) {
case Assignment assignment -> {
// 1) genereeri avaldise väärtustamise kood
// 2) genereeri väärtuse muutujasse salvestamise kood
}
case Block(List<Statement> statements) -> {
for (Statement stmt : statements) {
compileStatement(stmt);
}
}
case ExpressionStatement(Expression expression) -> {
// NB! Ära unusta, et ExpressionStatement viskab arvutatud väärtuse minema
}
case FunctionDefinition _ ->
throw new UnsupportedOperationException("cannot compile function definitions");
case IfStatement(Expression condition, Block thenBranch, Block elseBranch) -> {
Label doneLabel = new Label();
Label elseLabel = new Label();
// ...
}
case ReturnStatement _ -> throw new UnsupportedOperationException("cannot compile return statements");
case VariableDeclaration variableDeclaration -> {
}
case WhileStatement(Expression condition, Block body) -> {
}
}
}
private void compileExpression(Expression expression) {
switch (expression) {
case FunctionCall functionCall -> {
// genereeri argumentide väärtustamise kood
if (functionCall.isArithmeticOperation()) {
compileArithmeticOperation(functionCall);
} else if (functionCall.isComparisonOperation()) {
compileComparisonOperation(functionCall);
} else {
compileBuiltinFunction(functionCall);
}
}
case IntegerLiteral(Integer value) -> {
}
case StringLiteral(String value) -> throw new UnsupportedOperationException("cannot compile strings");
case Variable variable -> {
}
}
}
private void compileArithmeticOperation(FunctionCall functionCall) {
}
private void compileComparisonOperation(FunctionCall call) {
}
private void compileBuiltinFunction(FunctionCall functionCall) {
}
}