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.
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:
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:
* 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:
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:
* 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
| Forma | Equivalente | Executa se condição já for verdadeira? |
|---|---|---|
WITH TEST BEFORE | while | Não |
WITH TEST AFTER | do...while | Sim (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:
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:
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:
* 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:
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
* 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
* 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
* 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.