examples/calculator: add services to save and load from Json
[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 json::dynamic
19
20 # Hold the state of the calculator and its services
21 class CalculatorContext
22 # Result of the last operation
23 var result: nullable Numeric = null
24
25 # Last operation pushed with `push_op`, to be executed on the next push
26 var last_op: nullable Char = null
27
28 # Value currently being entered
29 var current: nullable FlatBuffer = null
30
31 # Text to display on screen
32 fun display_text: String
33 do
34 var result = result
35 var last_op = last_op
36 var current = current
37
38 var buf = new FlatBuffer
39
40 if result != null and (current == null or last_op != '=') then
41 if last_op == '=' then buf.append "= "
42
43 buf.append result.to_s
44 buf.add ' '
45 end
46
47 if last_op != null and last_op != '=' then
48 buf.add last_op
49 buf.add ' '
50 end
51
52 if current != null then
53 buf.append current.to_s
54 buf.add ' '
55 end
56
57 return buf.to_s
58 end
59
60 # Push operation `op`, will usually execute the last operation
61 fun push_op(op: Char)
62 do
63 apply_last_op_if_any
64 if op == 'C' then
65 self.result = null
66 last_op = null
67 else
68 last_op = op # store for next push_op
69 end
70
71 # prepare next current
72 self.current = null
73 end
74
75 # Push a digit
76 fun push_digit(digit: Int)
77 do
78 var current = current
79 if current == null then current = new FlatBuffer
80 current.add digit.to_s.chars.first
81 self.current = current
82
83 if last_op == '=' then
84 self.result = null
85 last_op = null
86 end
87 end
88
89 # Switch entry mode from integer to decimal
90 fun switch_to_decimals
91 do
92 var current = current
93 if current == null then current = new FlatBuffer.from("0")
94 if not current.chars.has('.') then current.add '.'
95 self.current = current
96 end
97
98 # Execute the last operation it not null
99 protected fun apply_last_op_if_any
100 do
101 var op = last_op
102
103 var result = result
104
105 var current = current
106 if current == null then current = new FlatBuffer
107
108 if op == null then
109 result = current.to_n
110 else if op == '+' then
111 result = result.add(current.to_n)
112 else if op == '-' then
113 result = result.sub(current.to_n)
114 else if op == '/' then
115 result = result.div(current.to_n)
116 else if op == '*' then
117 result = result.mul(current.to_n)
118 end
119
120 self.result = result
121 self.current = null
122 end
123
124 # Serialize calculator state to Json
125 fun to_json: String
126 do
127 # Do not save NaN nor inf
128 var result = self.result
129 if result != null and (result.to_f.is_nan or result.to_f.is_inf != 0) then result = null
130
131 var self_last_op = self.last_op
132 var last_op
133 if self_last_op == null then
134 last_op = "null"
135 else last_op = "\"{self_last_op}\""
136
137 var self_current = self.current
138 var current
139 if self_current == null then
140 current = "null"
141 else current = "\"{self_current}\""
142
143 return """
144 {
145 "result": {{{result or else "null"}}},
146 "last_op": {{{last_op}}},
147 "current": {{{current}}}
148 }"""
149 end
150
151 # Load calculator state from Json
152 init from_json(json_string: String)
153 do
154 var json = json_string.to_json_value
155 if json.is_error then
156 print "Loading state failed: {json.to_error}"
157 return
158 end
159
160 var result = json["result"]
161 if result.is_numeric then self.result = result.to_numeric
162
163 var last_op = json["last_op"]
164 if last_op.is_string then self.last_op = last_op.to_s.chars.first
165
166 var current = json["current"]
167 if current.is_string then self.current = new FlatBuffer.from(current.to_s)
168 end
169 end
170
171 redef universal Float
172 redef fun to_s do return to_precision(6)
173 end