Powerbuilder – Evaluating Arithmetic Expressions
Below is a function which allows the user to enter an arithmetic expression and then evaluate it and return the result. This will work for addition (+), subtraction(-), multiplication(*), division(/), and exponentiation(^). You can also enter parenthesis to change the order of precedence if needed. You would call this from where ever you like to validate and convert user entered expressions.
Example: you need to calculate the area of a circle the user types in. User would type “3.14*8^2” in the case of a circle with a radius of 8. Function would return a string of “200.96”
public function string uf_eval_arithmetic_expression (string infix_expr, integer precision);
// convert an expression into a numeric value (string in, number converted to string out)
//
decimal ld_result, ld_operand_stack [], ld_operand1, ld_operand2, ld_temp_result
string ls_infix_str, ls_currchar
string ls_infix_token [], ls_operator_stack [], ls_postfix_token [], ls_token
integer li_stack_top, li_itoken_sub, li_ptoken_sub
integer li_incoming_priority, li_instack_priority
char lc_temp_char
string ls_temp_string
ld_result = 0
//********************************************************************
// first check the syntax of the infix arithmetic expression and put
// each token into the ls_infix_token array
//********************************************************************
ls_infix_str = lefttrim (infix_expr) + "@"
ls_currchar = left (ls_infix_str, 1)
ls_token = ""
DO WHILE 1 = 1
CHOOSE CASE ls_currchar
CASE "@"
exit
CASE "^", "*", "/", "+", "-", "(", ")"
li_itoken_sub = li_itoken_sub + 1
ls_infix_token [li_itoken_sub] = ls_currchar
ls_infix_str = lefttrim (mid (ls_infix_str, 2))
ls_currchar = left (ls_infix_str, 1)
CASE '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'
DO WHILE 1 = 1
ls_token = ls_token + ls_currchar
ls_infix_str = Mid(ls_infix_str, 2)
lc_temp_char = Left(ls_infix_str, 1)
ls_temp_string = Mid(ls_infix_str, 2, 1)
IF lc_temp_char = " " AND IsNumber(ls_temp_string) THEN
ls_token = " "
Exit
END IF
ls_infix_str = LeftTrim(ls_infix_str)
ls_currchar = left (ls_infix_str, 1)
CHOOSE CASE ls_currchar
CASE "@", "^", "*", "/", "+", "-", "(", ")"
exit
END CHOOSE
LOOP
IF isnumber (ls_token) THEN
li_itoken_sub = li_itoken_sub + 1
ls_infix_token [li_itoken_sub] = ls_token
ls_token = ""
ELSE
//messagebox ("uf_eval_arithmetic_expression ", "invalid number")
return (infix_expr)
END IF
IF ls_currchar = "@" THEN
exit
END IF
CASE ELSE
//messagebox ("uf_eval_arithmetic_expression ", "invalid expression")
return (infix_expr)
END CHOOSE
LOOP
//********************************************************************
// second convert the infix expression into postfix notation
//********************************************************************
ls_infix_token [li_itoken_sub + 1] = "@"
li_stack_top = 0
li_itoken_sub = 0
li_ptoken_sub = 0
DO WHILE 1 = 1
li_itoken_sub = li_itoken_sub + 1
ls_token = ls_infix_token [li_itoken_sub]
CHOOSE CASE ls_token
CASE "@"
DO WHILE li_stack_top > 0
li_ptoken_sub = li_ptoken_sub + 1
ls_postfix_token [li_ptoken_sub] = ls_operator_stack [li_stack_top]
li_stack_top = li_stack_top - 1
LOOP
exit
CASE ")"
DO WHILE ls_operator_stack [li_stack_top] <> "("
li_ptoken_sub = li_ptoken_sub + 1
ls_postfix_token [li_ptoken_sub] = ls_operator_stack [li_stack_top]
li_stack_top = li_stack_top - 1
LOOP
li_stack_top = li_stack_top - 1
CASE "^", "*", "/", "+", "-", "("
DO WHILE 1 = 1
CHOOSE CASE ls_token
CASE "+", "-"
li_incoming_priority = 1
CASE "*", "/"
li_incoming_priority = 2
CASE ELSE
li_incoming_priority = 4
END CHOOSE
IF li_stack_top = 0 THEN
li_instack_priority = 0
ELSE
CHOOSE CASE ls_operator_stack [li_stack_top]
CASE "+", "-"
li_instack_priority = 1
CASE "*", "/"
li_instack_priority = 2
CASE "^"
li_instack_priority = 3
CASE ELSE
li_instack_priority = 0
END CHOOSE
END IF
IF li_incoming_priority > li_instack_priority THEN
exit
END IF
li_ptoken_sub = li_ptoken_sub + 1
ls_postfix_token [li_ptoken_sub] = ls_operator_stack [li_stack_top]
li_stack_top = li_stack_top - 1
LOOP
li_stack_top = li_stack_top + 1
ls_operator_stack [li_stack_top] = ls_token
CASE ELSE /* ls_token is an operand */
li_ptoken_sub = li_ptoken_sub + 1
ls_postfix_token [li_ptoken_sub] = ls_token
END CHOOSE
LOOP
//********************************************************************
// third evaluate the postfix arithmetic expression. each token is
// stored as a string occurance in the ls_postfix_token array
//********************************************************************
ls_postfix_token [li_ptoken_sub + 1] = "@"
ld_result = 0
li_stack_top = 0
li_ptoken_sub = 0
DO WHILE 1 = 1
li_ptoken_sub = li_ptoken_sub + 1
ls_token = ls_postfix_token [li_ptoken_sub]
CHOOSE CASE ls_token
CASE "@"
IF li_stack_top = 0 THEN
return ""
END IF
ld_result = round (ld_operand_stack [li_stack_top], precision)
return (string(ld_result, "0.####"))
CASE "^", "*", "/", "+", "-"
IF ls_token = "/" and ld_operand_stack [li_stack_top] = 0 THEN
//messagebox ("fx_eval_arith_expr", "divide by zero error")
return (infix_expr)
END IF
IF li_stack_top < 2 THEN
//messagebox ("uf_eval_arithmetic_expression ", "invalid expression")
return (infix_expr)
END IF
ld_operand1 = ld_operand_stack [li_stack_top - 1]
ld_operand2 = ld_operand_stack [li_stack_top]
CHOOSE CASE ls_token
CASE "+"
ld_temp_result = ld_operand1 + ld_operand2
CASE "-"
ld_temp_result = ld_operand1 - ld_operand2
CASE "*"
ld_temp_result = ld_operand1 * ld_operand2
CASE "/"
ld_temp_result = ld_operand1 / ld_operand2
CASE ELSE
ld_temp_result = ld_operand1 ^ ld_operand2
END CHOOSE
ld_operand_stack [li_stack_top - 1] = ld_temp_result
li_stack_top = li_stack_top - 1
CASE ELSE /* have an operand so add to stack */
li_stack_top = li_stack_top + 1
ld_operand_stack [li_stack_top] = dec (ls_token)
END CHOOSE
LOOP
Updated March 2021