package chocopy.pa2;

import chocopy.common.analysis.SymbolTable;
import chocopy.common.analysis.types.SymbolType;
import chocopy.common.astnodes.Program;

/** Top-level class for performing semantic analysis. */
public class StudentAnalysis {

    /** Perform semantic analysis on PROGRAM, adding error messages and
     *  type annotations. Provide debugging output iff DEBUG. Returns modified
     *  tree. */
    public static Program process(Program program, boolean debug) {
        if (program.hasErrors()) {
            return program;
        }

        //DeclarationAnalyzer declarationAnalyzer =
        //    new DeclarationAnalyzer(program.errors);
        //program.dispatch(declarationAnalyzer);
        //SymbolTable<SymbolType> globalSym =
        //    declarationAnalyzer.getGlobals();
        SymbolTable<SymbolType> globalSym = new SymbolTable<>();

        // TODO: I should run this part again and again, until symbolTable convergences.
        TypeChecker symBuilder = new TypeChecker(globalSym, program.errors, true);
        program.dispatch(symBuilder);
        // program.dispatch(symBuilder); // Twice run to update these use-before-decl globalDecl/nonlocalDecl symTable entry.
        // note: thanks to the silly grammar, it doesn't allow type-deduction.
        //       so we can support use-before-decl by only one extra run, rather  than many many times.
        //       now globalDecl and nonLocalDecl relies to the second run.
        //       but we can just allow globalDecl and nonLocalDecl to update symTable on the typeCheckerRun below!
        //       what a silly design!
        // def fuck():
        //     def shit():
        //         def func():
        //             nonlocal c
        //             # ...
        //         nonlocal b
        //         c:decltype(b) = None
        //     nonlocal a
        //     b:decltype(a) = None


        if(!program.hasErrors()) {
            TypeChecker next_time = new TypeChecker(symBuilder.getGlobals(), program.errors, false);
            program.dispatch(next_time);
        }

        return program;
    }
}
