calculator: implement unary minus
[nit.git] / examples / calculator / src / calculator_logic.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Business logic of a calculator
16 module calculator_logic
17
18 import serialization
19
20 # Hold the state of the calculator and its services
21 class CalculatorContext
22 auto_serializable
23
24 # Result of the last operation
25 var result: nullable Numeric = null
26
27 # Last operation pushed with `push_op`
28 var last_op: nullable Text = null
29
30 # Is `last_op` an unary operation or a '='?
31 var last_op_was_unary = false
32
33 # Value currently being entered
34 var current: nullable String = null
35
36 # Text to display on screen
37 fun display_text: String
38 do
39 var buf = new FlatBuffer
40
41 var last_op = last_op
42 var result = result
43 if result != null then
44 if last_op_was_unary then buf.append "{last_op or else "?"} "
45
46 buf.append result.to_s
47 buf.add ' '
48 end
49
50 if last_op != null and not last_op_was_unary then
51 buf.append last_op
52 buf.add ' '
53 end
54
55 var current = current
56 if current != null then
57 buf.append current.to_s
58 buf.add ' '
59 end
60
61 return buf.to_s
62 end
63
64 # Push operation `op`, will usually execute the last operation
65 fun push_op(op: Text)
66 do
67 # Constants
68 # TODO Protect constants to preserve full precision and to forbid appending extra digits
69 if op == "π" then
70 if last_op_was_unary then clear
71 current = pi.to_s
72 return
73 else if op == "e" then
74 if last_op_was_unary then clear
75 current = 2.718282.to_s
76 return
77
78 # Clear screen
79 else if op == "C" then
80 clear
81 return
82
83 # Unary -
84 else if op == "-" then
85 if current == null then
86 if last_op_was_unary then clear
87 current = "-"
88 return
89 else if current == "-" then
90 current = null
91 return
92 end
93 end
94
95 # For all operators, apply pending operators
96 apply_last_op_if_any
97
98 var result = self.result or else 0
99
100 last_op = op
101 last_op_was_unary = true
102
103 # Unary operators
104 if op == "√" then
105 self.result = result.to_f.sqrt
106 else if op == "x²" then
107 self.result = result.to_f.pow(2.0)
108 else if op == "x!" then
109 self.result = result.to_i.factorial
110 else if op == "sin" then
111 self.result = result.to_f.sin
112 else if op == "cos" then
113 self.result = result.to_f.cos
114 else if op == "tan" then
115 self.result = result.to_f.tan
116
117 # =
118 else if op == "=" then
119 current = null
120
121 # Binary operators
122 else
123 self.result = result # Set as same or 0
124 last_op_was_unary = false
125 current = null
126 end
127 end
128
129 # Clear all state
130 private fun clear
131 do
132 result = null
133 last_op = null
134 current = null
135 end
136
137 # Push a digit
138 fun push_digit(digit: Int)
139 do
140 if last_op_was_unary then clear
141
142 var current = current
143 if current == null then current = ""
144 current += digit.to_s
145 self.current = current
146 end
147
148 # Switch entry mode from integer to decimal
149 fun switch_to_decimals
150 do
151 var current = current
152 if current == null then current = "0"
153 if not current.chars.has('.') then current += "."
154 self.current = current
155 end
156
157 # Execute the last operation it not null
158 protected fun apply_last_op_if_any
159 do
160 var op = last_op
161 var result = result
162 var current = current
163 self.current = null
164
165 if current == null then return
166
167 if op == null then
168 result = current.to_n
169 else if result != null then
170 if op == "+" then
171 result = result.add(current.to_n)
172 else if op == "-" then
173 result = result.sub(current.to_n)
174 else if op == "/" or op == "÷" then
175 result = result.div(current.to_n)
176 else if op == "*" or op == "×" then
177 result = result.mul(current.to_n)
178 else if op == "%" then
179 result = result.to_i % current.to_i
180 else if op == "xⁿ" then
181 result = result.to_f.pow(current.to_f)
182 else if op == "log" then
183 result = result.to_f.log_base(current.to_f)
184 end
185 end
186
187 self.result = result
188 end
189 end
190
191 redef universal Float
192 redef fun to_s do return to_precision(6)
193 end