// Q: What's this? // A: This is a mini "home-made" calculator. You may also regard it as a very elementary version of "interpreter". import os const numeric_char = [`0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `.`, `e`, `E`] // Convert expression to Reverse Polish Notation. fn expr_to_rev_pol(expr string) ![]string { if expr == '' { return error('err: empty expression') } mut stack := []string{} mut rev_pol := []string{} mut pos := 0 for pos < expr.len { mut end_pos := pos for end_pos < expr.len && expr[end_pos] in numeric_char { end_pos++ } if end_pos > pos { stack << expr[pos..end_pos] pos = end_pos } else if end_pos == pos { op := expr[pos].ascii_str() match op { '(' { stack << op } '*', '/' { for stack.len > 0 && stack.last() !in ['(', '+', '-'] { rev_pol << stack.last() stack.delete(stack.len - 1) } stack << op } '+', '-' { for stack.len > 0 && stack.last() != '(' { rev_pol << stack.last() stack.delete(stack.len - 1) } stack << op } ')' { for stack.len > 0 && stack.last() != '(' { rev_pol << stack.last() stack.delete(stack.len - 1) } stack.delete(stack.len - 1) } else { return error('err: invalid character `${op}`') } } pos++ } } for stack.len > 0 { top := stack.last() rev_pol << top stack.delete(stack.len - 1) } return rev_pol } // Evaluate the result of Reverse Polish Notation. fn eval_rev_pol(rev_pol []string) !f64 { mut stack := []f64{} for item in rev_pol { if is_num_string(item) { stack << item.f64() } else { if stack.len >= 2 { oprand_r := stack.last() stack.delete(stack.len - 1) oprand_l := stack.last() stack.delete(stack.len - 1) match item { '+' { stack << oprand_l + oprand_r } '-' { stack << oprand_l - oprand_r } '*' { stack << oprand_l * oprand_r } '/' { if oprand_r == 0 { return error('err: divide by zero') } stack << oprand_l / oprand_r } else {} } } else { return error('err: invalid expression') } } } return stack[0] } fn is_num_string(str string) bool { for c in str { if c !in numeric_char { return false } } return true } fn main() { println('Please enter the expression you want to calculate, e.g. 1e2+(3-2.5)*6/1.5 .') println("Enter 'exit' or 'EXIT' to quit.") mut expr_count := 0 for { expr_count++ expr := os.input_opt('[${expr_count}] ') or { println('') break }.trim_space() if expr in ['exit', 'EXIT'] { break } rev_pol := expr_to_rev_pol(expr) or { eprintln(err) continue } res := eval_rev_pol(rev_pol) or { eprintln(err) continue } println(res) } }