REPORT YRESFORM.
PARAMETERS: PFORM(255).
DATA FRES TYPE F.
PERFORM RESOLVER_FORMULAEXP USING PFORM FRES 9.
WRITE: / 'Resultado: ', FRES.
FORM RESOLVER_FORMULAEXP USING PFORMULA PRESULTADO PNRDEC.
* Operadores válidos em pformula.
CONSTANTS VC_OPERADORES(5) VALUE '^*/+-'.
DATA: BEGIN OF ITAB_FORMULA OCCURS 0,
OPERADOR,
VALOR(16) TYPE P DECIMALS 9,
END OF ITAB_FORMULA.
DATA: VL_CARACTER, "Caracter da string pformula.
VL_POS TYPE I, "Posição da string pformula.
VL_VALOR(40), "Contém um operando da formula.
VL_VALOR1(16) TYPE P DECIMALS 9, "1º valor de uma operação.
VL_OPERADOR, "Operador matemático.
VL_VALOR2(16) TYPE P DECIMALS 9, "2º valor de uma operação.
VL_POSCARACTER TYPE I, "Posição da varredura de string.
VL_FORMULA2(255), "Variável auxiliar.
VL_VALORAUX(16) TYPE P DECIMALS 9, "Variável auxiliar.
VL_STRAUX(255), "Variável auxiliar.
VL_LENSTR TYPE P. "Tamanho em carac. de pformula.
************************************************************************
* Preenche a tabela itab_formula com valores e operadores.
* Suponhamos a seguinte formula: 4*6/2+1
* A tabela itab_formula conterá quatro registros da seguinte forma.
*
* 4,0000000000 +
* 6,0000000000 /
* 2,0000000000 +
* 1,0000000000
*
* Atenção: A tabela é preenchida até ser encontrada o final da fórmula
* ou um fecha parênteses ")".
************************************************************************
VL_LENSTR = STRLEN( PFORMULA ).
DO VL_LENSTR TIMES.
VL_POS = SY-INDEX - 1.
VL_CARACTER = PFORMULA+VL_POS(1).
" Encerra operação quando um fecha parênteses for encontrado.
IF VL_CARACTER = ')'.
EXIT.
ENDIF.
* Quando um abre parênteses for encontrado, processa tudo dentro dos
* parênteses chamando novamente a função de cálculo de fórmula.
IF VL_CARACTER = '('.
CLEAR VL_VALOR.
VL_POS = VL_POS + 1.
VL_FORMULA2 = PFORMULA+VL_POS.
PERFORM RESOLVER_FORMULAEXP USING VL_FORMULA2 VL_VALORAUX PNRDEC.
* Altera pformula para continuar sendo avaliada.
* O valor da avaliação dos parênteses (vl_valorAux) é colocado na variá-
* vel valor para que possa ser considerado.
VL_POS = VL_POS - 1.
IF VL_POS <> 0.
VL_STRAUX = PFORMULA(VL_POS).
ELSE.
CLEAR VL_STRAUX.
ENDIF.
VL_VALOR = VL_VALORAUX. "Considera resultado do parênteses.
CONCATENATE VL_STRAUX VL_FORMULA2 INTO PFORMULA.
VL_CARACTER = PFORMULA+VL_POS(1).
ENDIF.
* O Fecha parênteses no If serve quando estamos no final de uma expres-
* são entre parênteses, sendo que o último valor deve ser considerado.
IF VC_OPERADORES CA VL_CARACTER OR VL_CARACTER = ')'.
" Insere valor e operador na tabela interna.
CLEAR ITAB_FORMULA.
IF VL_CARACTER <> ')'.
ITAB_FORMULA-OPERADOR = VL_CARACTER.
ELSE.
"O fecha parênteses deve ser processado novamente.
CONCATENATE '0' PFORMULA INTO PFORMULA.
ENDIF.
ITAB_FORMULA-VALOR = VL_VALOR.
APPEND ITAB_FORMULA.
CLEAR VL_VALOR.
ELSE.
CONCATENATE VL_VALOR VL_CARACTER INTO VL_VALOR.
ENDIF.
ENDDO.
IF NOT VL_VALOR IS INITIAL.
CLEAR ITAB_FORMULA.
ITAB_FORMULA-VALOR = VL_VALOR.
APPEND ITAB_FORMULA.
ENDIF.
************************************************************************
* Realiza cálculo dos dados da tabela itab_formula. *
************************************************************************
* Elimina parte da string que já foi processada.
VL_POS = VL_POS + 1.
IF VL_POS <> VL_LENSTR.
PFORMULA = PFORMULA+VL_POS.
ELSE.
CLEAR PFORMULA.
ENDIF.
* Resolve fórmula contina na tabela itab_formula, considerando preceden-
* cia de operadores.
VL_POSCARACTER = 0.
WHILE VL_POSCARACTER < 5.
VL_CARACTER = VC_OPERADORES+VL_POSCARACTER(1).
READ TABLE ITAB_FORMULA WITH KEY OPERADOR = VL_CARACTER.
"Se trata-se de operador * OU +, a pesquisa deve ser diferente.
IF VL_CARACTER = '*'.
" Procura '*' ou '/'. O que encontrar primeiro é o que vale.
IF SY-SUBRC = 0.
VL_POS = SY-TABIX.
READ TABLE ITAB_FORMULA WITH KEY OPERADOR = '/'.
IF ( SY-SUBRC = 0 AND SY-TABIX > VL_POS ) OR SY-SUBRC <> 0.
READ TABLE ITAB_FORMULA INDEX VL_POS.
ENDIF.
ELSE.
READ TABLE ITAB_FORMULA WITH KEY OPERADOR = '-'.
ENDIF.
ELSEIF VL_CARACTER = '+'.
" Procura '+' ou '-'. O que encontrar primeiro é o que vale.
IF SY-SUBRC = 0.
VL_POS = SY-TABIX.
READ TABLE ITAB_FORMULA WITH KEY OPERADOR = '-'.
IF ( SY-SUBRC = 0 AND SY-TABIX > VL_POS ) OR SY-SUBRC <> 0.
READ TABLE ITAB_FORMULA INDEX VL_POS.
ENDIF.
ELSE.
READ TABLE ITAB_FORMULA WITH KEY OPERADOR = '-'.
ENDIF.
ENDIF.
IF SY-SUBRC = 0. "Realiza operação deste item com seu sucessor.
"Utiliza vl_pos para armazenar nº do registro da tabela interna+1.
VL_POS = SY-TABIX + 1.
VL_VALOR1 = ITAB_FORMULA-VALOR.
VL_OPERADOR = ITAB_FORMULA-OPERADOR.
READ TABLE ITAB_FORMULA INDEX VL_POS.
VL_VALOR2 = ITAB_FORMULA-VALOR.
CASE VL_OPERADOR.
WHEN '^'.
ITAB_FORMULA-VALOR = VL_VALOR1 ** VL_VALOR2.
WHEN '*'.
ITAB_FORMULA-VALOR = VL_VALOR1 * VL_VALOR2.
WHEN '/'.
IF VL_VALOR2 <> 0.
ITAB_FORMULA-VALOR = VL_VALOR1 / VL_VALOR2.
ELSE.
MESSAGE I010(ZSAPMZS01).
CLEAR PRESULTADO.
EXIT.
ENDIF.
WHEN '+'.
ITAB_FORMULA-VALOR = VL_VALOR1 + VL_VALOR2.
WHEN '-'.
ITAB_FORMULA-VALOR = VL_VALOR1 - VL_VALOR2.
ENDCASE.
* Efetua arredondamento do resultado conforme PNRDEC.
ITAB_FORMULA-VALOR = ITAB_FORMULA-VALOR * ( 10 ** PNRDEC ).
ITAB_FORMULA-VALOR = TRUNC( ITAB_FORMULA-VALOR ).
ITAB_FORMULA-VALOR = ITAB_FORMULA-VALOR / ( 10 ** PNRDEC ).
MODIFY ITAB_FORMULA INDEX VL_POS.
VL_POS = VL_POS - 1.
DELETE ITAB_FORMULA INDEX VL_POS.
ELSE.
VL_POSCARACTER = VL_POSCARACTER + 1.
ENDIF.
ENDWHILE.
PRESULTADO = ITAB_FORMULA-VALOR.
ENDFORM. " RESOLVER_FORMULAEXP