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