v/examples/mini_calculator.v

136 lines
2.7 KiB
V

// 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)
}
}