Translate

Mostrar mensagens com a etiqueta COBOL 6.5. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta COBOL 6.5. Mostrar todas as mensagens

quarta-feira, 28 de fevereiro de 2024

Programas COBOL Aninhados: Os Holocrons Secretos Escondidos Dentro de um Único Load Module

 

Bellacosa Mainframe e o caso do programa COBOL com varios pgms aninhados

Programas COBOL Aninhados: Os Holocrons Secretos Escondidos Dentro de um Único Load Module

Quando o Padawan Descobre que um Programa COBOL Pode Conter Outros Programas COBOL em Seu Interior

Por Bellacosa Mainframe

"Nem todo programa precisa viver sozinho. Alguns mestres escondem seus aprendizes dentro do próprio templo."

Mestre Bellacosa Sysprog Jedi

Durante décadas, boa parte dos desenvolvedores COBOL aprendeu uma arquitetura bastante simples:

Programa A

CALL Programa B

CALL Programa C

GOBACK

Fim.

Era o modelo clássico.

Cada programa em um membro.

Cada módulo separado.

Cada compilação independente.

Cada load module vivendo sua própria vida no PDS ou PDSE.

Mas então o jovem Padawan encontra algo estranho em um código legado:

IDENTIFICATION DIVISION.
PROGRAM-ID. CLIENTES.

...

IDENTIFICATION DIVISION.
PROGRAM-ID. VALIDA-CPF.

...

IDENTIFICATION DIVISION.
PROGRAM-ID. CALCULA-IDADE.

Ele arregala os olhos.

Pensa:

Mestre...

Tem três programas no mesmo fonte...

Isso é magia negra?

Não.

É COBOL.

E existe há muito tempo.

Poucos desenvolvedores modernos utilizam essa funcionalidade.

Menos ainda entendem completamente como ela funciona.

E alguns veteranos passam a carreira inteira sem escrever um único programa aninhado.

Hoje vamos abrir esse antigo holocron.


O que é um Programa Aninhado?

Em COBOL, um programa pode conter outros programas.

Algo semelhante a:

Programa principal

  • Programa filho A

  • Programa filho B

  • Programa filho C

Tudo dentro do mesmo fonte.

Exemplo:

PROGRAMA PRINCIPAL


PROGRAMA FILHO


PROGRAMA NETO

Hierarquia.

Muito parecido com:

Java

Inner Class

Python

Nested Function

Pascal

Nested Procedures

COBOL possui conceito semelhante.


Quando Surgiu?

Programas aninhados apareceram nas especificações modernas do COBOL.

Principalmente:

COBOL 85

Posteriormente aprimorados em:

Enterprise COBOL V3

V4

V5

V6

Hoje são totalmente suportados.

COBOL 6.5

IBM z16

IBM z17


Estrutura Básica

Exemplo.

Programa Principal

IDENTIFICATION DIVISION.
PROGRAM-ID. CLIENTE.

Programa interno

IDENTIFICATION DIVISION.
PROGRAM-ID. VALIDA.

Outro

IDENTIFICATION DIVISION.
PROGRAM-ID. CALCULA.

Tudo junto.


Exemplo Completo

Programa Pai

IDENTIFICATION DIVISION.
PROGRAM-ID. CLIENTE.


DATA DIVISION.


WORKING-STORAGE SECTION.


01 WS-CPF.
PIC X(11).



PROCEDURE DIVISION.


MOVE '12345678901'
TO WS-CPF


CALL 'VALIDA'


DISPLAY "FIM"



STOP RUN.

Programa Filho

IDENTIFICATION DIVISION.
PROGRAM-ID. VALIDA.


PROCEDURE DIVISION.


DISPLAY 'VALIDANDO'


EXIT PROGRAM.

Fim.


Exemplo Realista

Vamos construir.

Sistema cadastro.

Programa principal

CADASTRO

Filhos

VALIDA-CPF

VALIDA-DATA

CALCULA-IDADE

GERA-LOG


Passo 1

Programa principal

PROGRAM-ID. CADASTRO.

Passo 2

WS

01 WS-CPF.
01 WS-DATA.
01 WS-IDADE.

Passo 3

Chamar programas internos

CALL 'VALIDA-CPF'

CALL 'VALIDA-DATA'

CALL 'CALCULA-IDADE'

Passo 4

Definir programa interno

IDENTIFICATION DIVISION.
PROGRAM-ID. VALIDA-CPF.

Passo 5

Lógica

IF CPF NUMERIC

DISPLAY 'OK'

ELSE

DISPLAY 'ERRO'

Passo 6

Encerrar

EXIT PROGRAM.

Passo 7

Novo programa

PROGRAM-ID. CALCULA-IDADE.

Tudo dentro do mesmo fonte.


Como COBOL Enxerga Isso?

COBOL vê:

Programa Pai

Programa Filho

Programa Neto

Estrutura hierárquica.


Como é Chamado?

Nested Program

Programa Interno

Contained Program

Outer Program

Inner Program


Como Funciona na Memória?

Aqui a magia começa.

Suponha:

CADASTRO

contém

VALIDA

contém

LOG


Carregamento

LOAD MODULE


CADASTRO


VALIDA


LOG

Tudo junto.

Mesmo módulo.


Não existe:

Busca catálogo

Load library

Fetch

Link

Já está residente.


Vantagem

Muito rápida.


Visualmente

Programa separado

CALL


LOAD


FETCH


EXECUTA

Programa interno

CALL


EXECUTA

Menos overhead.


Compartilhamento de Variáveis

Esse é o recurso mais poderoso.

Programa interno pode acessar dados externos.

Exemplo

Programa pai

01 WS-NOME.
PIC X(30).

Filho

DISPLAY WS-NOME.

Sem USING.

Sem linkage.

Sem parâmetro.

Sem copiar.


Mágica?

Não.

Escopo léxico.


Exemplo

Pai

MOVE 'BELLACOSA'

TO WS-NOME

Filho

DISPLAY WS-NOME

Resultado

BELLACOSA

Por Que Isso Existe?

Reduzir acoplamento.

Criar rotinas privadas.

Encapsulamento.

Organização.


É semelhante a:

Método privado Java

Função interna Python

Classe interna


Curiosidade

Muitos programadores COBOL nem sabem que isso existe.

Porque nos bancos normalmente vemos:

CALL externo

PDS

Loadlib

Arquitetura tradicional.


Exemplo de Encapsulamento

Programa externo

Atendimento.

Internos

Valida

Log

Criptografa

Audita

Ninguém pode chamar diretamente.


Somente programa pai.


Segurança

Excelente vantagem.

Programa externo:

QUALQUER UM CHAMA

Interno

SÓ O PAI CHAMA

Maior controle.


Performance

Muito boa.

Programa já está carregado.

Sem I/O.

Sem busca.

Sem FETCH.


Pode economizar milhares de chamadas.


Existe Desvantagem?

Sim.

Grande.


Fonte gigantesco.

50000 linhas.

100000 linhas.

Difícil manutenção.


Problema do Monstro Cósmico

Exemplo

CADASTRO


VALIDA


IDADE


LOG


EMAIL


TOKEN


PIX


SMS


AUDITORIA


JSON


XML


MQ


DB2

Tudo junto.

Virou um kaiju.


Dificuldade de Reuso

Programa interno não pode ser facilmente reutilizado.

Outro sistema quer usar.

Não consegue.


Terá que copiar.

Ou refatorar.


Debug

Pode confundir.

Call stack enorme.

Nested levels.


IBM Debug Tool ajuda.

Fault Analyzer também.


Cuidados

1 Não exagerar

Máximo recomendado:

3 níveis

Pai

Filho

Neto

Mais que isso:

Dor.


2 Documentar

Quem chama quem.


3 Evitar dependência excessiva

Filho acessando 500 WS.

Ruim.


4 Preferir USING

Mesmo podendo acessar WS externa.

Melhor:

USING WS-CPF

Mais claro.


5 Não esconder regras críticas

Exemplo

Cálculo juros.

Pode dificultar auditoria.


Como Compilar?

Normal.

IGYCRCTL

Enterprise COBOL

Sem segredo.


Compilador entende estrutura.

Gera módulo único.


Programa Recursivo Aninhado

Sim.

É possível.

Filho pode ser:

RECURSIVE


Exemplo

PROGRAM-ID. FATORIAL.


RECURSIVE.

Dentro do pai.


Muito elegante.

Pouco usado.


CALL Estático Interno

Muito eficiente.

CALL 'VALIDA-CPF'

Sem carga.

Sem fetch.


Comparação

CaracterísticaPrograma ExternoPrograma Interno
ReusoExcelenteBaixo
SegurançaMédiaAlta
PerformanceBoaExcelente
EncapsulamentoMédioExcelente
DebugFácilMédio
OrganizaçãoBoaBoa
ManutençãoExcelentePode degradar
DistribuiçãoFácilLimitada

Quando Vale a Pena?

Eu costumo ensinar aos Padawans uma regra simples.

Use programas aninhados quando a rotina fizer sentido apenas dentro daquele programa principal.

Exemplos:

✅ Validação interna

✅ Máscara

✅ Auditoria

✅ Log

✅ Conversão local

✅ Parser pequeno


Evite para:

❌ Acesso DB2

❌ MQ

❌ APIs

❌ Serviços compartilhados

❌ Criptografia corporativa

❌ Regras usadas por dezenas de sistemas


O Conselho Final do Mestre Bellacosa

Programas aninhados em COBOL são quase como compartimentos secretos escondidos em um gigantesco cruzador estelar IBM Z. Eles oferecem encapsulamento, velocidade, organização e um nível de proteção natural contra reutilização indevida.

Mas, assim como qualquer artefato poderoso do universo dos Sysprogs Jedi, devem ser usados com sabedoria.

Um pequeno programa interno de validação pode tornar um sistema elegante e fácil de entender.

Dez programas internos profundamente dependentes das variáveis do pai podem transformar um módulo em um labirinto digno de um antigo datacenter abandonado, onde cada alteração provoca medo, regressões e longas noites analisando dumps.

A filosofia Bellacosa Mainframe é simples:

Aninhe comportamentos, não sistemas inteiros.

Encapsule segredos, não complexidade desnecessária.

E lembre-se sempre: se o Padawan precisar de três cafés, dois dumps e um Fault Analyzer para entender a estrutura do programa, talvez o lado sombrio da manutenção já tenha vencido.


 

segunda-feira, 22 de janeiro de 2024

COBOL Recursivo: Quando o Padawan Descobre que um Programa Pode Invocar a Si Mesmo

 

Bellacosa Mainframe apresenta um programa cobol recursivo

COBOL Recursivo: Quando o Padawan Descobre que um Programa Pode Invocar a Si Mesmo

O Poder Proibido do CALL Infinito na Galáxia IBM Z

Por Bellacosa Mainframe

"O medo leva ao ABEND. O ABEND leva ao dump. O dump leva ao sofrimento."

— Mestre Sysprog Yoda/390

Durante muitos anos, programadores COBOL cresceram acreditando em uma verdade absoluta:

"Programa COBOL não faz recursão."

Era praticamente um dogma do templo Jedi dos mainframes.

E, honestamente?

Durante décadas isso foi quase verdade.

A maioria dos sistemas bancários, seguradoras, governo e processamento batch nunca precisou de algoritmos recursivos.

Mas então surgiram:

  • XML

  • JSON

  • árvores hierárquicas

  • parsers

  • estruturas dinâmicas

  • APIs modernas

  • integração com Java

  • serviços z/OS Connect

E um dia o jovem Padawan perguntou:

"Mestre Bellacosa...

Posso fazer um programa COBOL chamar ele mesmo?"

A resposta é:

Sim.

Mas como todo poder da Força, existe um preço.


O que é Recursividade?

Recursão significa:

Um procedimento invoca ele próprio.

Exemplo clássico:

Calcular fatorial.

5! = 5 × 4 × 3 × 2 × 1

Matematicamente:

Fatorial(n)

se n=1 retorna 1

senão

n * Fatorial(n-1)

Exemplo:

F(5)

5 × F(4)

5 × 4 × F(3)

5 × 4 × 3 × F(2)

5 × 4 × 3 × 2 × F(1)

120

É uma técnica extremamente elegante.


COBOL Sempre Teve Recursão?

Não.

Historicamente COBOL nasceu em 1959.

E sua filosofia era:

Processamento comercial.

Arquivos.

Registros.

Batch.

Volumes enormes.

Não havia necessidade prática.

Durante décadas COBOL assumia:

Programa residente

Uma única Working Storage

Compartilhada

Exemplo:

WORKING-STORAGE SECTION.

01 CONTADOR PIC 9(5).

Só existe uma instância.

Todo mundo usa a mesma.

Problema:

Programa chama ele mesmo.

Segunda chamada sobrescreve variáveis.

Primeira execução quebra.

Caos.


Quando Surgiu?

IBM Enterprise COBOL começou suportar adequadamente recursão através da opção:

RECURSIVE

ou

RENT

Dependendo da geração do compilador.

Hoje encontramos suporte em:

Enterprise COBOL 4

Enterprise COBOL 5

Enterprise COBOL 6.x

COBOL 6.3

COBOL 6.4

COBOL 6.5

Totalmente suportado.

Inclusive no IBM z17.


Como Declarar um Programa Recursivo

No cabeçalho:

IDENTIFICATION DIVISION.
PROGRAM-ID. FATORIAL RECURSIVE.

ou

PROGRAM-ID. FATORIAL.

RECURSIVE.

Depende do compilador.


Exemplo Completo

Passo 1

Definir LINKAGE

LINKAGE SECTION.


01 LK-NUMERO.
   PIC 9(4).

01 LK-RESULTADO.
   PIC 9(10).

Passo 2

Procedure Division

PROCEDURE DIVISION USING
          LK-NUMERO
          LK-RESULTADO.

Passo 3

Caso base

IF LK-NUMERO <= 1

   MOVE 1 TO LK-RESULTADO

ELSE

...

Passo 4

Criar variável local

LOCAL-STORAGE SECTION.


01 WS-TEMP.
   PIC 9(10).

01 WS-N.
   PIC 9(4).

Local Storage é fundamental.

Já veremos por quê.


Passo 5

Preparar próxima chamada

COMPUTE WS-N = LK-NUMERO -1

Passo 6

Invocar a si mesmo

CALL 'FATORIAL'

USING

WS-N
WS-TEMP

Passo 7

Calcular

COMPUTE LK-RESULTADO =
        LK-NUMERO * WS-TEMP

Fim.


Programa Completo

IDENTIFICATION DIVISION.
PROGRAM-ID. FATORIAL RECURSIVE.

DATA DIVISION.


LOCAL-STORAGE SECTION.


01 WS-N.
   PIC 9(4).

01 WS-TEMP.
   PIC 9(10).


LINKAGE SECTION.


01 LK-NUMERO.
   PIC 9(4).

01 LK-RESULTADO.
   PIC 9(10).


PROCEDURE DIVISION USING
        LK-NUMERO
        LK-RESULTADO.


IF LK-NUMERO <= 1

   MOVE 1 TO LK-RESULTADO

ELSE

   COMPUTE WS-N =
           LK-NUMERO -1


   CALL 'FATORIAL'

   USING

      WS-N
      WS-TEMP


   COMPUTE LK-RESULTADO =
           LK-NUMERO * WS-TEMP

END-IF.


GOBACK.

O que Acontece na Memória?

Aqui mora a magia.

Imagine:

FATORIAL(5)

Stack:

Frame 1
n=5

Chama:

Frame 2
n=4

Depois:

Frame 3
n=3

Depois:

Frame 4
n=2

Depois:

Frame 5
n=1

Retorno:

Frame 5 → 1

Frame 4 → 2

Frame 3 → 6

Frame 2 →24

Frame 1 →120


Visualmente

STACK



F(5)

F(4)

F(3)

F(2)

F(1)

Depois:

POP


F(1)


F(2)


F(3)


F(4)


F(5)

O Papel da LOCAL-STORAGE

Muitos Padawans cometem um erro fatal.

Usar:

WORKING-STORAGE

Exemplo:

01 WS-N.

Errado.

Por quê?

Porque Working Storage é única.

Compartilhada.

Exemplo:

Primeira chamada

WS-N=5

Segunda

WS-N=4

Terceira

WS-N=3

Sobrescreve.

Tudo quebra.


LOCAL-STORAGE

Cria uma cópia nova.

Para cada chamada.

Exemplo:

Frame 1

WS-N=5

Frame 2

WS-N=4

Frame 3

WS-N=3

Independentes.


Como o Compilador Faz Isso?

Ele cria activation records.

Também chamados:

Stack frames

Contém:

Variáveis locais

Endereço retorno

Parâmetros

Estado execução

Exatamente igual:

C

C++

Java

Pascal


CALL Estático

Mais eficiente.

CALL 'FATORIAL'

Resolvido no linkedit.

Menos overhead.


CALL Dinâmico

MOVE 'FATORIAL'

TO WS-PGM


CALL WS-PGM

Mais flexível.

Menos rápido.


Existe Limite?

Sim.

STACK.

Região LE.

Memory limits.

Exemplo:

F(1000000)

Boom.

SOC1

SOC4

S878

S80A

Dependendo ambiente.


Como Evitar?

Caso base.

Sempre.

Sempre.

Sempre.

Exemplo ruim:

CALL FATORIAL


CALL FATORIAL


CALL FATORIAL

Nunca termina.


Cuidados Importantes

1 Não usar Working Storage

Errado.


2 Ter condição parada

Obrigatório.


3 Validar entrada

Exemplo:

IF NUMERO < 0

Fatorial negativo?

Não faz sentido.


4 Cuidado com profundidade

100 níveis

Ok.

500

Talvez.

10000

Perigoso.


5 Testar LE Runtime

HEAP

STACK

ALL31

Importante.


Performance

Aqui muitos ficam decepcionados.

Recursão é elegante.

Mas nem sempre rápida.

Cada chamada cria:

Frame

Parâmetros

Contexto

Retorno

Overhead.


Loop tradicional:

PERFORM


UNTIL

Quase sempre vence.


Exemplo

Fatorial iterativo.

MOVE 1 TO WS-TOTAL


PERFORM VARYING I


FROM 1


BY 1


UNTIL I > N


MULTIPLY I


BY WS-TOTAL


END-PERFORM

Muito eficiente.


Quando Vale a Pena?

Árvores.

XML.

JSON.

DOM.

Grafos.

Estruturas hierárquicas.

Menus.

Organogramas.

Filesystem.

Dependências.


Exemplo XML

empresa


 departamento


   funcionario


 departamento


   funcionario

Percorrer árvore.

Recursão fica linda.


Segurança

Sim.

Existe aspecto de segurança.

Ataque:

Stack exhaustion.

Negação de serviço.

DoS.

Programa recebe:

99999999

Explode stack.


Proteção:

IF NIVEL > 1000

DISPLAY "ERRO"

STOP RUN

Debug

Debug recursivo é divertido.

E assustador.

Ferramentas:

IBM Debug Tool

Fault Analyzer

Abend Aid

Mostram:

Call Stack

Frame atual

Parâmetros

Muito útil.


Curiosidades

Poucos sistemas bancários usam recursão pesada.

COBOL nasceu para processamento sequencial.

Por isso muitos programadores veteranos passam décadas sem escrever uma única rotina recursiva.


Outra curiosidade:

COBOL OO suporta métodos recursivos naturalmente.

INVOKE SELF

Também funciona.


Recursão de Cauda (Tail Recursion)

Alguns compiladores modernos fazem otimizações.

Mas COBOL IBM geralmente não realiza Tail Call Optimization de forma agressiva como linguagens funcionais.

Logo:

RETURN F(N-1)

Ainda pode consumir stack.

Não confie nisso.


Padrão Recomendado pelo Mestre Bellacosa

Para o jovem Padawan COBOL, eu costumo ensinar uma regra bastante prática:

Use recursão apenas quando ela deixar a solução mais clara do que um PERFORM VARYING.

Se a lógica for:

  • Fatorial

  • Somatório

  • Contador

Prefira iteração.

Se a lógica envolver:

  • Árvores

  • XML

  • JSON aninhado

  • Catálogos IMS

  • Dependências complexas

  • Estruturas organizacionais

  • Menus multiníveis

Então a recursividade torna-se uma excelente aliada.


Considerações Finais

A recursão em COBOL é quase como encontrar um antigo holocron escondido nos corredores de um datacenter IBM Z.

Muitos veteranos nunca precisaram utilizá-la. Outros a evitam por receio de consumir stack, degradar performance ou provocar abends difíceis de diagnosticar.

No entanto, compreender programas recursivos amplia significativamente o repertório técnico de um desenvolvedor COBOL moderno. Em um mundo onde o mainframe conversa diariamente com APIs REST, processa documentos JSON complexos, integra-se com microsserviços e participa de arquiteturas híbridas, dominar recursividade deixa de ser apenas curiosidade acadêmica e passa a ser uma habilidade estratégica.

O verdadeiro Padawan não utiliza recursão para demonstrar inteligência. Ele a utiliza porque compreende profundamente o problema, conhece os limites da memória, respeita a pilha de execução do Language Environment, escolhe LOCAL-STORAGE sabiamente e sabe exatamente quando um simples PERFORM VARYING é a solução mais elegante.

E essa talvez seja a maior lição do COBOL recursivo:

Nem todo poder disponível deve ser usado. Mas todo poder disponível deve ser compreendido.

No templo Bellacosa Mainframe, conhecer a recursividade significa possuir mais uma ferramenta no cinto utilitário do Sysprog Jedi, pronta para ser acionada quando a missão exigir atravessar estruturas tão profundas quanto os corredores infinitos de uma galáxia IBM Z em plena era do z17.