Kompilaatori kodutöö, vihje 2
package week10;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import week9.AktkBinding;
import week7.AktkAst;
import week7.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 {
    public 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
        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) {
        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 AktkCompilerVisitor(mv).visit(ast);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
        // klassi lõpetamine
        cw.visitEnd();
        // klassi baidijada genereerimine
        return cw.toByteArray();
    }
    private static class AktkCompilerVisitor extends AstVisitor.VoidVisitor {
        private final MethodVisitor mv;
        private final Map<VariableBinding, Integer> variableIndices = new HashMap<>();
        private AktkCompilerVisitor(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);
        }
        @Override
        protected void visitVoid(Assignment assignment) {
            // 1) genereeri avaldise väärtustamise kood
            // 2) genereeri väärtuse muutujasse salvestamise kood
        }
        @Override
        protected void visitVoid(Block block) {
            for (Statement stmt : block.getStatements()) {
                visit(stmt);
            }
        }
        @Override
        protected void visitVoid(ExpressionStatement expressionStatement) {
            // NB! Ära unusta, et ExpressionStatement viskab arvutatud väärtuse minema
        }
        @Override
        protected void visitVoid(FunctionCall functionCall) {
            // genereeri argumentide väärtustamise kood
            if (functionCall.isArithmeticOperation()) {
                compileArithmeticOperation(functionCall);
            }
            else if (functionCall.isComparisonOperation()) {
                compileComparisonOperation(functionCall);
            }
            else {
                compileBuiltinFunction(functionCall);
            }
        }
        private void compileArithmeticOperation(FunctionCall functionCall) {
        }
        private void compileComparisonOperation(FunctionCall call) {
        }
        private void compileBuiltinFunction(FunctionCall functionCall) {
        }
        @Override
        protected void visitVoid(FunctionDefinition functionDefinition) {
            throw new UnsupportedOperationException("cannot compile function definitions");
        }
        @Override
        protected void visitVoid(IfStatement ifStatement) {
            Label doneLabel = new Label();
            Label elseLabel = new Label();
            // ...
        }
        @Override
        protected void visitVoid(IntegerLiteral integerLiteral) {
        }
        @Override
        protected void visitVoid(ReturnStatement returnStatement) {
            throw new UnsupportedOperationException("cannot compile return statements");
        }
        @Override
        protected void visitVoid(StringLiteral stringLiteral) {
            throw new UnsupportedOperationException("cannot compile strings");
        }
        @Override
        protected void visitVoid(Variable variable) {
        }
        @Override
        protected void visitVoid(VariableDeclaration variableDeclaration) {
        }
        @Override
        protected void visitVoid(WhileStatement whileStatement) {
        }
    }
}