32c2f72a09cfd43afdf5750375ebdf41295d2291
[nit.git] / examples / calculator.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import gtk
18
19 class CalculatorContext
20 var result : nullable Float = null
21
22 var last_op : nullable Char = null
23
24 var current : nullable Float = null
25 var after_point : nullable Int = null
26
27 fun push_op( op : Char )
28 do
29 apply_last_op_if_any
30 if op == 'C' then
31 self.result = 0.0
32 last_op = null
33 else
34 last_op = op # store for next push_op
35 end
36
37 # prepare next current
38 after_point = null
39 current = null
40 end
41
42 fun push_digit( digit : Int )
43 do
44 var current = current
45 if current == null then current = 0.0
46
47 var after_point = after_point
48 if after_point == null then
49 current = current * 10.0 + digit.to_f
50 else
51 current = current + digit.to_f * 10.0.pow(after_point.to_f)
52 self.after_point -= 1
53 end
54
55 self.current = current
56 end
57
58 fun switch_to_decimals
59 do
60 if after_point != null then return
61
62 after_point = -1
63 end
64
65 fun apply_last_op_if_any
66 do
67 var op = last_op
68
69 var result = result
70 if result == null then result = 0.0
71
72 var current = current
73 if current == null then current = 0.0
74
75 if op == null then
76 result = current
77 else if op == '+' then
78 result = result + current
79 else if op == '-' then
80 result = result - current
81 else if op == '/' then
82 result = result / current
83 else if op == '*' then
84 result = result * current
85 end
86 self.result = result
87 self.current = null
88 end
89 end
90
91 class CalculatorGui
92 super GtkCallable
93
94 var win : GtkWindow
95 var container : GtkGrid
96
97 var lbl_disp : GtkLabel
98 var but_eq : GtkButton
99 var but_dot : GtkButton
100
101 var context = new CalculatorContext
102
103 redef fun signal( sender, user_data )
104 do
105 var after_point = context.after_point
106 if after_point == null then
107 after_point = 0
108 else
109 after_point = (after_point.abs)
110 end
111
112 if user_data isa Char then # is an operation
113 var c = user_data
114 if c == '.' then
115 but_dot.set_sensitive= false
116 context.switch_to_decimals
117 lbl_disp.text = "{context.current.to_i}."
118 else
119 but_dot.set_sensitive= true
120 context.push_op( c )
121
122 var s = context.result.to_precision_native(6)
123 var index : nullable Int = null
124 for i in s.length.times do
125 var chiffre = s[i]
126 if chiffre == '0' and index == null then
127 index = i
128 else if chiffre != '0' then
129 index = null
130 end
131 end
132 if index != null then
133 s = s.substring(0, index)
134 if s[s.length-1] == ',' then s = s.substring(0, s.length-1)
135 end
136 lbl_disp.text = s
137 end
138 else if user_data isa Int then # is a number
139 var n = user_data
140 context.push_digit( n )
141 lbl_disp.text = context.current.to_precision_native(after_point)
142 end
143 end
144
145 init
146 do
147 init_gtk
148
149 win = new GtkWindow( 0 )
150
151 container = new GtkGrid(5,5,true)
152 win.add( container )
153
154 lbl_disp = new GtkLabel( "_" )
155 container.attach( lbl_disp, 0, 0, 5, 1 )
156
157 # digits
158 for n in [0..9] do
159 var but = new GtkButton.with_label( n.to_s )
160 but.request_size( 64, 64 )
161 but.signal_connect( "clicked", self, n )
162 if n == 0 then
163 container.attach( but, 0, 4, 1, 1 )
164 else container.attach( but, (n-1)%3, 3-(n-1)/3, 1, 1 )
165 end
166
167 # operators
168 var r = 1
169 for op in ['+', '-', '*', '/' ] do
170 var but = new GtkButton.with_label( op.to_s )
171 but.request_size( 64, 64 )
172 but.signal_connect( "clicked", self, op )
173 container.attach( but, 3, r, 1, 1 )
174 r+=1
175 end
176
177 # =
178 but_eq = new GtkButton.with_label( "=" )
179 but_eq.request_size( 64, 64 )
180 but_eq.signal_connect( "clicked", self, '=' )
181 container.attach( but_eq, 4, 3, 1, 2 )
182
183 # .
184 but_dot = new GtkButton.with_label( "." )
185 but_dot.request_size( 64, 64 )
186 but_dot.signal_connect( "clicked", self, '.' )
187 container.attach( but_dot, 1, 4, 1, 1 )
188
189 #C
190 var but_c = new GtkButton.with_label( "C" )
191 but_c.request_size( 64, 64 )
192 but_c.signal_connect("clicked", self, 'C')
193 container.attach( but_c, 2, 4, 1, 1 )
194
195 win.show_all
196 end
197 end
198
199 # context tests
200 var context = new CalculatorContext
201 context.push_digit( 1 )
202 context.push_digit( 2 )
203 context.push_op( '+' )
204 context.push_digit( 3 )
205 context.push_op( '*' )
206 context.push_digit( 2 )
207 context.push_op( '=' )
208 var r = context.result.to_precision( 2 )
209 assert r == "30.00" else print r
210
211 context = new CalculatorContext
212 context.push_digit( 1 )
213 context.push_digit( 4 )
214 context.switch_to_decimals
215 context.push_digit( 1 )
216 context.push_op( '*' )
217 context.push_digit( 3 )
218 context.push_op( '=' )
219 r = context.result.to_precision( 2 )
220 assert r == "42.30" else print r
221
222 context.push_op( '+' )
223 context.push_digit( 1 )
224 context.push_digit( 1 )
225 context.push_op( '=' )
226 r = context.result.to_precision( 2 )
227 assert r == "53.30" else print r
228
229 context = new CalculatorContext
230 context.push_digit( 4 )
231 context.push_digit( 2 )
232 context.switch_to_decimals
233 context.push_digit( 3 )
234 context.push_op( '/' )
235 context.push_digit( 3 )
236 context.push_op( '=' )
237 r = context.result.to_precision( 2 )
238 assert r == "14.10" else print r
239
240 #test multiple decimals
241 context = new CalculatorContext
242 context.push_digit( 5 )
243 context.push_digit( 0 )
244 context.switch_to_decimals
245 context.push_digit( 1 )
246 context.push_digit( 2 )
247 context.push_digit( 3 )
248 context.push_op( '+' )
249 context.push_digit( 1 )
250 context.push_op( '=' )
251 r = context.result.to_precision( 3 )
252 assert r == "51.123" else print r
253
254 #test 'C' button
255 context = new CalculatorContext
256 context.push_digit( 1 )
257 context.push_digit( 0 )
258 context.push_op( '+' )
259 context.push_digit( 1 )
260 context.push_digit( 0 )
261 context.push_op( '=' )
262 context.push_op( 'C' )
263 r = context.result.to_precision( 1 )
264 assert r == "0.0" else print r
265
266 # graphical application
267
268 if "NIT_TESTING".environ != "true" then
269 var app = new CalculatorGui
270 run_gtk
271 end