// 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 pospos { stack << expr[pos..end_pos] pos = end_pos } else if end_pos==pos { op := expr[pos].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++ print('[$expr_count] ') expr := os.get_line().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) } }