1. PERFORM básico — chamando parágrafos

O uso mais simples do PERFORM: chamar um parágrafo pelo nome, executá-lo e voltar para a linha seguinte. É o equivalente a uma chamada de função em outras linguagens.

COBOL PERFORM simples
       PROCEDURE DIVISION.

       0000-INICIO.
           PERFORM 1000-INICIALIZA
           PERFORM 2000-PROCESSA
           PERFORM 3000-FINALIZA
           STOP RUN.

       1000-INICIALIZA.
           INITIALIZE WS-DADOS
           MOVE ZEROS TO WS-CONTADOR.

       2000-PROCESSA.
           MOVE 'EM PROCESSAMENTO' TO WS-STATUS.

       3000-FINALIZA.
           MOVE 'CONCLUIDO' TO WS-STATUS.

🦕 Analogia — PERFORM é uma lista de tarefas

Imagine que o parágrafo 0000-INICIO é o chefe que organiza o trabalho. Ele não faz nada diretamente — só delega: "Vai lá inicializar, depois processa, depois finaliza." Cada parágrafo é um funcionário especializado que faz sua parte e devolve o controle para o chefe. Essa separação torna o código muito mais fácil de entender e manter.

2. PERFORM THRU

Executa todos os parágrafos em sequência, do primeiro ao último especificado:

COBOL PERFORM THRU
       PERFORM 2000-VALIDA THRU 2000-VALIDA-FIM

             * Executa 2000-VALIDA e todos os parágrafos até 2000-VALIDA-FIM

       2000-VALIDA.
           IF WS-CPF = SPACES
               MOVE 'N' TO WS-VALIDO
           END-IF.

       2000-VALIDA-SALDO.
           IF WS-SALDO < 0
               MOVE 'N' TO WS-VALIDO
           END-IF.

       2000-VALIDA-FIM.
           EXIT.

⚠️ Cuidado com PERFORM THRU em código legado

O PERFORM THRU executa todos os parágrafos físicos entre o primeiro e o último, independente do que são. Se alguém inserir um parágrafo novo no meio do código, ele passa a ser executado automaticamente — sem nenhum aviso. Use o padrão de ter um parágrafo NOME-FIM com apenas EXIT como delimitador explícito, e seja cuidadoso ao reorganizar o código.

3. PERFORM n TIMES

Repete um parágrafo (ou bloco inline) um número fixo de vezes:

COBOL PERFORM TIMES
             * Literal: repete exatamente 10 vezes
       PERFORM 2000-PROCESSA-LINHA 10 TIMES

             * Variável: repete o número de vezes definido em WS-QTD
       PERFORM 2000-PROCESSA-LINHA WS-QTD-REGISTROS TIMES

             * Inline com TIMES
       PERFORM 5 TIMES
           ADD 1 TO WS-CONTADOR
           PERFORM 2100-GRAVA-LINHA
       END-PERFORM

4. PERFORM UNTIL

Repete enquanto a condição for falsa — encerra quando ela se tornar verdadeira. É o laço mais usado em COBOL, especialmente para processar arquivos:

COBOL PERFORM UNTIL
       WORKING-STORAGE SECTION.
       01  WS-FIM-ARQUIVO    PIC X(01).
           88  FIM-ARQUIVO       VALUE 'S'.
           88  NAO-FIM-ARQUIVO   VALUE 'N'.

       PROCEDURE DIVISION.
       2000-PROCESSA-ARQUIVO.
           MOVE 'N' TO WS-FIM-ARQUIVO
           READ ARQ-ENTRADA
               AT END SET FIM-ARQUIVO TO TRUE
           END-READ

           PERFORM UNTIL FIM-ARQUIVO
               PERFORM 2100-TRATA-REGISTRO
               READ ARQ-ENTRADA
                   AT END SET FIM-ARQUIVO TO TRUE
               END-READ
           END-PERFORM.

💗 Para iniciantes — UNTIL é "até que", não "enquanto"

Em outras linguagens você escreve while (condição verdadeira) — repete enquanto for verdade. Em COBOL o PERFORM UNTIL repete até que a condição seja verdadeira, ou seja, enquanto ela for falsa. É o oposto do while. Então PERFORM UNTIL FIM-ARQUIVO significa "repita até chegar no fim do arquivo" — fica bem natural quando você usa level 88.

5. WITH TEST BEFORE e WITH TEST AFTER

Controla quando a condição do UNTIL é verificada — antes ou depois de executar o bloco:

COBOL TEST BEFORE vs TEST AFTER
             * TEST BEFORE (padrão): verifica ANTES de executar
             * Se a condição já for verdadeira, nunca executa
       PERFORM WITH TEST BEFORE UNTIL WS-CONTADOR > 10
           ADD 1 TO WS-CONTADOR
       END-PERFORM
             * equivalente ao while(contador <= 10) de outras linguagens

             * TEST AFTER: verifica DEPOIS de executar
             * Executa pelo menos uma vez, mesmo se a condição já for verdadeira
       PERFORM WITH TEST AFTER UNTIL WS-RESPOSTA = 'S'
           PERFORM 3000-PERGUNTA-USUARIO
       END-PERFORM
             * equivalente ao do { ... } while() de outras linguagens
FormaEquivalenteExecuta se condição já for verdadeira?
WITH TEST BEFOREwhileNão
WITH TEST AFTERdo...whileSim (1 vez)

6. PERFORM VARYING

O loop com contador — equivalente ao for de outras linguagens. Inicializa uma variável, define o incremento e a condição de parada:

COBOL PERFORM VARYING
       WORKING-STORAGE SECTION.
       01  WS-IDX       PIC 9(03) VALUE 0.
       01  WS-TABELA.
           05  WS-ITEM  PIC X(20) OCCURS 10 TIMES.

       PROCEDURE DIVISION.
             * Percorre os 10 itens da tabela
       PERFORM VARYING WS-IDX FROM 1 BY 1
                       UNTIL WS-IDX > 10
           DISPLAY WS-ITEM(WS-IDX)
       END-PERFORM

             * Contagem regressiva: FROM 10 BY -1
       PERFORM VARYING WS-IDX FROM 10 BY -1
                       UNTIL WS-IDX < 1
           DISPLAY WS-ITEM(WS-IDX)
       END-PERFORM

             * Chamando parágrafo com VARYING
       PERFORM 2100-PROCESSA-ITEM
           VARYING WS-IDX FROM 1 BY 1
           UNTIL WS-IDX > WS-QTD-ITENS

🦕 Analogia — PERFORM VARYING é um controle de passos

Pense no PERFORM VARYING como um operador de elevador antigo: "Comece no andar 1, suba um andar por vez, pare quando passar do andar 10." O FROM é o andar inicial, o BY é quantos andares sobe por vez (pode ser negativo para descer), e o UNTIL é o andar de destino.

7. PERFORM VARYING com AFTER — loop aninhado

Para iterar sobre estruturas bidimensionais (como uma matriz), o COBOL permite dois contadores em um único PERFORM com a cláusula AFTER:

COBOL PERFORM VARYING com AFTER
       WORKING-STORAGE SECTION.
       01  WS-LINHA     PIC 9(02).
       01  WS-COLUNA    PIC 9(02).
       01  WS-MATRIZ.
           05  WS-CELULA  PIC 9(05)
               OCCURS 5 TIMES       *5 linhas
               INDEXED BY IDX-L
               OCCURS 3 TIMES       *3 colunas
               INDEXED BY IDX-C.

       PROCEDURE DIVISION.
             * Percorre todas as 15 células (5 linhas × 3 colunas)
       PERFORM VARYING WS-LINHA FROM 1 BY 1
                       UNTIL WS-LINHA > 5
           AFTER WS-COLUNA FROM 1 BY 1
                 UNTIL WS-COLUNA > 3
               PERFORM 2000-PROCESSA-CELULA
       END-PERFORM
             * O índice interno (WS-COLUNA) é reiniciado a cada iteração do externo

8. PERFORM inline

Quando o bloco a repetir é curto e não precisa de um parágrafo separado, você pode escrever o código diretamente dentro do PERFORM:

COBOL PERFORM inline
             * PERFORM inline simples — sem nomear parágrafo
       PERFORM UNTIL WS-CONTADOR > 100
           ADD 1             TO WS-CONTADOR
           ADD WS-CONTADOR  TO WS-SOMA
       END-PERFORM

             * PERFORM inline com VARYING
       PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 10
           MOVE SPACES TO WS-ITEM(WS-I)
       END-PERFORM

✅ Inline ou parágrafo separado — quando usar cada um?

  • Use PERFORM inline para blocos pequenos (2 a 5 linhas) que você não vai reutilizar
  • Use PERFORM com parágrafo quando o bloco for maior, precisar de nome claro, ou for chamado de mais de um lugar
  • Em sistemas bancários, prefira parágrafos separados — facilitam o debug e a leitura do dump em caso de abend

9. Organização de parágrafos

A convenção de numeração de parágrafos em COBOL é uma das boas práticas mais importantes. O padrão mais comum em sistemas bancários brasileiros usa prefixos numéricos que indicam o nível hierárquico:

COBOL Convenção de numeração
       PROCEDURE DIVISION.

             *=============================================================*
             * 0000 — CONTROLE PRINCIPAL                                   *
             *=============================================================*
       0000-INICIO.
           PERFORM 1000-INICIALIZA
           PERFORM 2000-PROCESSA
           PERFORM 3000-FINALIZA
           STOP RUN.

             *-------------------------------------------------------------*
             * 1000 — INICIALIZAÇÃO                                        *
             *-------------------------------------------------------------*
       1000-INICIALIZA.
           INITIALIZE WS-DADOS
           PERFORM 1100-ABRE-ARQUIVOS
           PERFORM 1200-LE-PARAMETROS.

       1100-ABRE-ARQUIVOS.
           OPEN INPUT  ARQ-ENTRADA
           OPEN OUTPUT ARQ-SAIDA.

       1200-LE-PARAMETROS.
           READ ARQ-PARAM INTO WS-PARAM
               AT END MOVE 'S' TO WS-SEM-PARAM
           END-READ.

             *-------------------------------------------------------------*
             * 2000 — PROCESSAMENTO PRINCIPAL                              *
             *-------------------------------------------------------------*
       2000-PROCESSA.
           PERFORM 2100-LE-REGISTRO
           PERFORM UNTIL FIM-ARQUIVO
               PERFORM 2200-VALIDA-REGISTRO
               IF REGISTRO-VALIDO
                   PERFORM 2300-TRATA-REGISTRO
               ELSE
                   PERFORM 2400-TRATA-INVALIDO
               END-IF
               PERFORM 2100-LE-REGISTRO
           END-PERFORM.

             *-------------------------------------------------------------*
             * 3000 — FINALIZAÇÃO                                          *
             *-------------------------------------------------------------*
       3000-FINALIZA.
           PERFORM 3100-FECHA-ARQUIVOS
           PERFORM 3200-IMPRIME-TOTAIS.

             *-------------------------------------------------------------*
             * 9000 — TRATAMENTO DE ERROS                                  *
             *-------------------------------------------------------------*
       9000-TRATA-ERRO.
           DISPLAY 'ERRO: ' WS-MSG-ERRO
           MOVE 8 TO RETURN-CODE
           STOP RUN.

🟣 Para quem já programa — a lógica dos prefixos

A numeração não é só estética. Ela comunica intenção: 0000 é o maestro (nunca tem lógica de negócio), 1000 é inicialização, 2000–6000 é processamento (subrotinas com 2100, 2200...), 7000–8000 costuma ser I/O, e 9000 é reservado para erros e exceções. Quando você lê um dump de abend e vê que parou em 9200-TRATA-FILE-STATUS, já sabe que é um erro de arquivo sem nem precisar ver o código.

10. Erros comuns

1. Loop infinito com PERFORM UNTIL

COBOL Loop infinito — causa clássica
             * BUG: WS-FIM nunca é atualizado dentro do loop
       PERFORM UNTIL FIM-ARQUIVO
           PERFORM 2000-PROCESSA
                 * Esqueceu de ler o próximo registro!
                 * FIM-ARQUIVO nunca vira 'S' → loop eterno
       END-PERFORM

             * CORRETO: sempre lê o próximo registro no final do loop
       PERFORM UNTIL FIM-ARQUIVO
           PERFORM 2000-PROCESSA
           READ ARQ-ENTRADA
               AT END SET FIM-ARQUIVO TO TRUE
           END-READ
       END-PERFORM

2. PERFORM VARYING sem atualizar a variável de controle

COBOL VARYING com variável zerada dentro do loop
             * BUG: MOVE ZEROS dentro do loop reseta o contador!
       PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 10
           PERFORM 2000-PROCESSA
           INITIALIZE WS-AREA-TRABALHO  *se WS-I estiver em WS-AREA-TRABALHO...
       END-PERFORM                        *...WS-I vai a zero → loop infinito!

             * Atenção: o INITIALIZE zera TODOS os campos numéricos do grupo
             * Certifique-se de que o índice do VARYING não esteja no grupo

3. STOP RUN dentro de parágrafo chamado por PERFORM

COBOL STOP RUN em sub-rotina
             * OK em parágrafos de erro: STOP RUN encerra o programa
       9000-TRATA-ERRO.
           DISPLAY 'ABEND: ' WS-MSG
           MOVE 12 TO RETURN-CODE
           STOP RUN.   *encerra tudo — intencional

             * Evite STOP RUN em parágrafos de processamento normal —
             * dificulta o entendimento do fluxo e o teste unitário

⚠️ GOBACK vs. STOP RUN

Em programas chamados por outros programas via CALL (sub-programas), use GOBACK em vez de STOP RUN. O STOP RUN encerra toda a sessão — o programa chamador também para. O GOBACK retorna apenas para quem chamou, mantendo o programa pai em execução.