sexta-feira, 15 de fevereiro de 2013

3. Cadê minha subrotina?

Como encontrar um objeto do tipo Subroutine cujo nome lógico é diferente do nome físico desse objeto?

Considere que a subrotina ISNUMGT0 (criada no post nº 1) tenha sido salva com o nome físico S12345AB. Assim, num programa qualquer que use essa subrotina, teríamos algo do tipo:

...
PERFORM ISNUMGT0 #NUM #EH-NUM
...

Porém, ao editarmos ISNUMGT0 (comando "E ISNUMGT0"), não achamos objeto algum. Como proceder então? É simples. Ao editar, basta acrescentar a opção F, assim: E F ISNUMGT0

Até a próxima.

terça-feira, 15 de maio de 2012

2. Dica de debug

Como percorrer os objetos que compõem um programa à medida que são executados?

Isso facilita a identificação de objetos de interesse (por exemplo, o nome de um mapa que se deseja alterar o layout) e a localização de erros.

Segue o passo-a-passo:

1) Digite TEST ON na linha de comando para ativar o modo de teste;

2) Na linha de comando, digite: TEST SET BP ALL BEG (o que equivale a colocar um breakpoint no início de cada objeto da biblioteca atual);

3) Por fim, ainda na linha de comando, digite MYPROG para iniciar a execução do seu programa;

4) A partir de então, à medida que cada breakpoint é atingido, abre-se uma janela onde se pode optar por listar o código-fonte bem na linha do breakpoint (opção L), ou avançar para o próximo breakpoint (ou seja, para o próximo objeto da sequência de chamadas) via opção G.

OBS: Quando um breakpoint é atingido e listamos o código-fonte correspondente, podemos executar o programa passo a passo a partir desse breakpoint usando a tecla F2, ou podemos avançar para o próximo breakpoint digitando o comando GO.

OBS: Para apagar todos os breakpoints, digite, na linha de comando: TEST DE BP **

OBS: Para dasativar o modo de teste, digite, na linha de comando: TEST OFF

Até a próxima.

segunda-feira, 12 de março de 2012

1. Validando campos numéricos de mapas

Como verificar se o valor de um campo alfa do mapa (tela) é numérico e maior que zero?

Tenho visto comumente a seguinte abordagem em vários programas analisados, e o que é pior, para cada campo a ser validado, essa lógica se repete...

1 #VALOR-A (A10)
1 #AUX (A10)
1 REDEFINE #AUX
  2 #AUX-N (N10)
...
IF #VALOR-A = ' '
  REINPUT ...
END-IF
*
MOVE RIGHT #VALOR-A TO #AUX
EXAMINE #AUX FOR ' ' REPLACE '0'
EXAMINE #AUX FOR '_' REPLACE '0'
*
IF #AUX <> MASK(NNNNNNNNNN) OR #AUX-N <= 0
  REINPUT ...
END-IF

O problema óbvio dessa abordagem é que uma entrada do tipo "1_0" ou "1 0" será interpretada erroneamente como "100", além da já mencionada duplicação de código.

A solução é desenvolver uma rotina genérica que valide isso, independente do tamanho do campo do mapa.

A primeira rotina valida apenas se o campo é numérico...

DEFINE DATA
PARAMETER
/* 29 EH O TAMANHO MAXIMO PARA UM CAMPO NUMERICO
/* ATUALMENTE. 'BY VALUE' EH FUNDAMENTAL AQUI
/* PARA QUE A ROTINA SEJA GENERICA.
1 #VALOR-A (A29) BY VALUE
1 #IS-NUMERIC (L)
*
LOCAL
1 #I (N2)
1 #CHAR (A1)
1 #MAX-LEN (N2) CONST<29>
END-DEFINE
*
DEFINE SUBROUTINE ISNUM
#IS-NUMERIC := FALSE
*
IF #VALOR-A = ' '
  ESCAPE ROUTINE
END-IF
*
MOVE RIGHT #VALOR-A TO #VALOR-A
*
/* SUBSTITUI ESPACOS E UNDERLINES A ESQUERDA
/* POR ZEROS.
FOR #I 1 TO #MAX-LEN
  MOVE SUBSTRING(#VALOR-A, #I, 1) TO #CHAR
*
  IF #CHAR <> '_' AND #CHAR <> ' '
    IF #CHAR = '-' AND #I <> #MAX-LEN /* POE O SINAL NA 1A POSICAO.
      MOVE '0' TO SUBSTRING(#VALOR-A, #I, 1)
      MOVE '-' TO SUBSTRING(#VALOR-A, 1, 1)
    END-IF
*
    ESCAPE BOTTOM
  ELSE
    MOVE '0' TO SUBSTRING(#VALOR-A, #I, 1)
*
    IF #I = #MAX-LEN /* SO HA ESPACOS E UNDERLINES.
      ESCAPE ROUTINE
    END-IF
  END-IF
END-FOR
*
IF #VALOR-A IS (N29)
  #IS-NUMERIC := TRUE
END-IF
END-SUBROUTINE
END

Note que se faz necessário substituir espaços e underlines à esquerda por zeros para que valores do tipo "_100__" sejam considerados numéricos, mas não valores do tipo "_10_0_".

A segunda rotina valida se o campo é numérico e maior que zero...

DEFINE DATA
PARAMETER
1 #VALOR-A (A29) BY VALUE
1 REDEFINE #VALOR-A
  2 #VALOR (N29)
1 #IS-NUMERIC-GT-0 (L)
*
LOCAL
1 #IS-NUMERIC (L)
END-DEFINE
*
DEFINE SUBROUTINE ISNUMGT0
#IS-NUMERIC-GT-0 := FALSE
*
/* PRIMEIRO, VERIFICA SE O CAMPO EH NUMERICO.
PERFORM ISNUM #VALOR-A #IS-NUMERIC
*
IF #IS-NUMERIC
  MOVE RIGHT #VALOR-A TO #VALOR-A
  EXAMINE #VALOR-A FOR ' ' REPLACE '0'
  EXAMINE #VALOR-A FOR '_' REPLACE '0'
*
  /* VERIFICA SE O CAMPO EH MAIOR QUE ZERO.
  /* 'IS (N29)' REJEITA VALORES DO TIPO
  /* '0-111111111111111111111111111'.
  IF #VALOR-A IS (N29) AND #VALOR > 0
    #IS-NUMERIC-GT-0 := TRUE
  END-IF
END-IF
END-SUBROUTINE
END

A seguir, temos um exemplo de utilização da rotina ISNUMGT0 a partir de outro objeto Natural.

...
LOCAL
#VALOR-A (A17)
1 #EH-NUM-MAIOR-QUE-0 (L)
...
PERFORM ISNUMGT0 #VALOR-A #EH-NUM-MAIOR-QUE-0
IF NOT #EH-NUM-MAIOR-QUE-0
  REINPUT 'CAMPO DEVE SER NUMERICO E MAIOR QUE ZERO' MARK *#VALOR-A
END-IF

Até a próxima.