public void gera(
      ASAPrograma asa,
      PrintWriter saida,
      String nomeClasseJava,
      boolean geraCodigoParaInterrupcaoDeThread,
      boolean geraCodigoParaPontosDeParada,
      boolean geraCodigoParaInspecaoDeSimbolos)
      throws ExcecaoVisitaASA, IOException {

    PreCompilador preCompilador = new PreCompilador();
    asa.aceitar(preCompilador);

    VisitorGeracaoCodigo gerador =
        new VisitorGeracaoCodigo(
            asa,
            saida,
            geraCodigoParaInterrupcaoDeThread,
            geraCodigoParaPontosDeParada,
            geraCodigoParaInspecaoDeSimbolos);

    int totalVariaveis = asa.getTotalVariaveisDeclaradas();
    int totalVetores = asa.getTotalVetoresDeclarados();
    int totalMatrizes = asa.getTotalMatrizesDeclaradas();

    gerador
        .geraPackage("programas")
        .pulaLinha()
        .geraImportacaoPara(ErroExecucao.class)
        .geraImportacaoPara(Programa.class)
        .geraImportacaoDasBibliotecasIncluidas()
        .pulaLinha()
        .geraNomeDaClasse(nomeClasseJava)
        .geraChaveDeAberturaDaClasse()
        .pulaLinha()
        .geraAtributosParaAsBibliotecasIncluidas()
        .pulaLinha()
        .geraAtributosParaAsVariaveisGlobais()
        .pulaLinha()
        .geraAtributosParaAsVariaveisPassadasPorReferencia(
            preCompilador.getVariaveisPassadasPorReferencia())
        .pulaLinha()
        .geraConstrutor(nomeClasseJava, totalVariaveis, totalVetores, totalMatrizes)
        .pulaLinha()
        .geraInicializacaoVariaveisGlobais()
        .pulaLinha()
        .geraMetodos(preCompilador.getFuncoesQuerForamInvocadas())
        .geraChaveDeFechamentoDaClasse();
  }
    private VisitorGeracaoCodigo geraImportacaoDasBibliotecasIncluidas() {
      for (NoInclusaoBiblioteca no : asa.getListaInclusoesBibliotecas()) {
        saida.append("import ").append(PACOTE_DAS_LIBS).append(no.getNome()).append(";").println();
      }

      return this;
    }
    public VisitorGeracaoCodigo geraAtributosParaAsBibliotecasIncluidas() {
      List<NoInclusaoBiblioteca> libsIncluidas = asa.getListaInclusoesBibliotecas();
      for (NoInclusaoBiblioteca biblioteca : libsIncluidas) {
        geradorAtributo.gera(biblioteca, saida, nivelEscopo);
      }

      if (!libsIncluidas.isEmpty()) {
        saida.println(); // deixa uma linha em branco depois dos atributos das bibliotecas
      }

      return this;
    }
 private List<NoDeclaracaoInicializavel> getVariaveisGlobaisDeclaradas(
     ASAPrograma asa, boolean excluiConstantes) {
   List<NoDeclaracao> declaracoesGlobais = asa.getListaDeclaracoesGlobais();
   List<NoDeclaracaoInicializavel> variaveisGlobais = new ArrayList<>();
   for (NoDeclaracao global : declaracoesGlobais) {
     if (global instanceof NoDeclaracaoInicializavel) {
       NoDeclaracaoInicializavel variavel = (NoDeclaracaoInicializavel) global;
       if (!(excluiConstantes && variavel.constante())) {
         variaveisGlobais.add(variavel);
       }
     }
   }
   return variaveisGlobais;
 }
 private void inicializaVariaveisGlobaisQueSaoPassadasPorReferencia() throws ExcecaoVisitaASA {
   List<NoDeclaracao> declaracoes = asa.getListaDeclaracoesGlobais();
   for (NoDeclaracao declaracao : declaracoes) {
     if (declaracao instanceof NoDeclaracaoVariavel) {
       NoDeclaracaoVariavel variavel = (NoDeclaracaoVariavel) declaracao;
       if (variavel.ehPassadaPorReferencia() && variavel.temInicializacao()) {
         String nomeTipo = Utils.getNomeTipoJava(variavel.getTipoDado());
         saida.append(Utils.geraIdentacao(nivelEscopo));
         saida.format(
             "REFS_%s[%s] = ", nomeTipo.toUpperCase(), Utils.geraStringIndice(variavel));
         variavel.getInicializacao().aceitar(this);
         saida.append(";").println(); // o ponto e vírgula depois da inicialização
       }
     }
   }
 }
 public VisitorGeracaoCodigo geraMetodos(Set<NoDeclaracaoFuncao> funcoesQueForamInvocadas)
     throws ExcecaoVisitaASA {
   List<NoDeclaracao> declaracoes = asa.getListaDeclaracoesGlobais();
   for (NoDeclaracao declaracao : declaracoes) {
     if (declaracao instanceof NoDeclaracaoFuncao) {
       NoDeclaracaoFuncao declaracaoFuncao = (NoDeclaracaoFuncao) declaracao;
       if (declaracaoFuncao.getNome().equals("inicio")
           || funcoesQueForamInvocadas.contains(
               declaracaoFuncao)) // só gera código para funções que foram invocadas
       {
         geradorDeclaracaoMetodo.gera(
             declaracaoFuncao,
             saida,
             this,
             nivelEscopo,
             gerandoCodigoParaInterrupcaoDeThread,
             gerandoCodigoParaPontosDeParada,
             gerandoCodigoParaInspecaoDeSimbolos,
             seed);
       }
     }
   }
   return this;
 }