v / examples / mini_calculator.v
136 lines · 130 sloc · 2.74 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Q: What's this?
2// A: This is a mini "home-made" calculator. You may also regard it as a very elementary version of "interpreter".
3import os
4
5const numeric_char = [`0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `.`, `e`, `E`]
6
7// Convert expression to Reverse Polish Notation.
8fn expr_to_rev_pol(expr string) ![]string {
9 if expr == '' {
10 return error('err: empty expression')
11 }
12 mut stack := []string{}
13 mut rev_pol := []string{}
14 mut pos := 0
15 for pos < expr.len {
16 mut end_pos := pos
17 for end_pos < expr.len && expr[end_pos] in numeric_char {
18 end_pos++
19 }
20 if end_pos > pos {
21 stack << expr[pos..end_pos]
22 pos = end_pos
23 } else if end_pos == pos {
24 op := expr[pos].ascii_str()
25 match op {
26 '(' {
27 stack << op
28 }
29 '*', '/' {
30 for stack.len > 0 && stack.last() !in ['(', '+', '-'] {
31 rev_pol << stack.last()
32 stack.delete(stack.len - 1)
33 }
34 stack << op
35 }
36 '+', '-' {
37 for stack.len > 0 && stack.last() != '(' {
38 rev_pol << stack.last()
39 stack.delete(stack.len - 1)
40 }
41 stack << op
42 }
43 ')' {
44 for stack.len > 0 && stack.last() != '(' {
45 rev_pol << stack.last()
46 stack.delete(stack.len - 1)
47 }
48 stack.delete(stack.len - 1)
49 }
50 else {
51 return error('err: invalid character `${op}`')
52 }
53 }
54
55 pos++
56 }
57 }
58 for stack.len > 0 {
59 top := stack.last()
60 rev_pol << top
61 stack.delete(stack.len - 1)
62 }
63 return rev_pol
64}
65
66// Evaluate the result of Reverse Polish Notation.
67fn eval_rev_pol(rev_pol []string) !f64 {
68 mut stack := []f64{}
69 for item in rev_pol {
70 if is_num_string(item) {
71 stack << item.f64()
72 } else {
73 if stack.len >= 2 {
74 oprand_r := stack.last()
75 stack.delete(stack.len - 1)
76 oprand_l := stack.last()
77 stack.delete(stack.len - 1)
78 match item {
79 '+' {
80 stack << oprand_l + oprand_r
81 }
82 '-' {
83 stack << oprand_l - oprand_r
84 }
85 '*' {
86 stack << oprand_l * oprand_r
87 }
88 '/' {
89 if oprand_r == 0 {
90 return error('err: divide by zero')
91 }
92 stack << oprand_l / oprand_r
93 }
94 else {}
95 }
96 } else {
97 return error('err: invalid expression')
98 }
99 }
100 }
101 return stack[0]
102}
103
104fn is_num_string(str string) bool {
105 for c in str {
106 if c !in numeric_char {
107 return false
108 }
109 }
110 return true
111}
112
113fn main() {
114 println('Please enter the expression you want to calculate, e.g. 1e2+(3-2.5)*6/1.5 .')
115 println("Enter 'exit' or 'EXIT' to quit.")
116 mut expr_count := 0
117 for {
118 expr_count++
119 expr := os.input_opt('[${expr_count}] ') or {
120 println('')
121 break
122 }.trim_space()
123 if expr in ['exit', 'EXIT'] {
124 break
125 }
126 rev_pol := expr_to_rev_pol(expr) or {
127 eprintln(err)
128 continue
129 }
130 res := eval_rev_pol(rev_pol) or {
131 eprintln(err)
132 continue
133 }
134 println(res)
135 }
136}
137