Kompilaatori kodutöö, vihje 2
package week12;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import week11.AktkBinding;
import week9.AktkAst;
import week9.ast.*;
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.Map;
import static org.objectweb.asm.Opcodes.*;
public class AktkCompiler {
private final AstNode ast;
private Map<VariableBinding, Integer> variableIndices;
private MethodVisitor mv;
public AktkCompiler(AstNode ast) {
this.ast = ast;
}
public static void main(String[] args) throws IOException {
// lihtsam viis "käsurea parameetrite andmiseks":
//args = new String[] {"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 = new String(Files.readAllBytes(sourceFile), "UTF-8");
// parsin ja moodustan AST'i
AstNode ast = AktkAst.createAst(source);
// seon muutujad
AktkBinding.bind(ast);
// kompileerin
byte[] bytes = createClass(ast, className);
Files.write(classFile, bytes);
}
public static byte[] createClass(AstNode ast, String className) {
return new AktkCompiler(ast).createClass(className);
}
private byte[] createClass(String className) {
variableIndices = new HashMap<>();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// Klassi attribuudid
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", null);
cw.visitSource(null, null);
// main meetod
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
generateCode(ast);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// klassi lõpetamine
cw.visitEnd();
// klassi baidijada genereerimine
return cw.toByteArray();
}
private int getVariableIndex(VariableBinding binding) {
Integer index = variableIndices.get(binding);
if (index != null) {
return index;
}
else {
// muutuja 0 on meie main-meetodi parameeter
index = variableIndices.size()+1;
variableIndices.put(binding, index);
return index;
}
}
private void generateCode(AstNode node) {
if (node instanceof Block) {
for (Statement stmt : ((Block)node).getStatements()) {
generateCode(stmt);
}
}
else if (node instanceof Assignment) {
// 1) genereeri avaldise väärtustamise kood
// 2) genereeri väärtuse muutujasse salvestamise kood
}
else if (node instanceof ExpressionStatement) {
// NB! Ära unusta, et ExpressionStatement viskab arvutatud väärtuse minema
}
else if (node instanceof IntegerLiteral) {
}
else if (node instanceof Variable) {
}
else if (node instanceof VariableDeclaration) {
}
else if (node instanceof FunctionCall) {
FunctionCall call = (FunctionCall) node;
if (call.getFunctionName().equals("-") && call.getArguments().size() == 1) {
// unaarne miinus
}
else if (call.isArithmeticOperation()) {
}
else if (call.isComparisonOperation()) {
}
else {
}
}
else if (node instanceof IfStatement) {
IfStatement stmt = (IfStatement) node;
Label doneLabel = new Label();
Label elseLabel = new Label();
// ...
}
else if (node instanceof WhileStatement) {
}
else {
assert node instanceof StringLiteral;
throw new UnsupportedOperationException("Seda konstrutsiooni praegu me ei toeta");
}
}
private void generateNormalFunctionCall(FunctionCall call) {
}
private void generateArithmeticOrLogicOperation(FunctionCall call) {
}
private void generateComparisonOperation(FunctionCall call) {
// JVM int-ide võrdlusoperatsioonide valikus on kõik operatsioonid seotud jumpidega.
// Kui sa ei taha avaldise väärtustamise koodi siduda if-lausega
// siis ma kasuta jumpe lihtsalt selleks, et tekitada stacki tippu
// kas 0 või 1
}
}