# Business logic of a calculator
module calculator_logic
+import serialization
+
# Hold the state of the calculator and its services
class CalculatorContext
+ auto_serializable
+
# Result of the last operation
var result: nullable Numeric = null
- # Last operation pushed with `push_op`, to be executed on the next push
- var last_op: nullable Char = null
+ # Last operation pushed with `push_op`
+ var last_op: nullable Text = null
+
+ # Is `last_op` an unary operation or a '='?
+ var last_op_was_unary = false
# Value currently being entered
- var current: nullable FlatBuffer = null
+ var current: nullable String = null
# Text to display on screen
fun display_text: String
do
- var result = result
- var last_op = last_op
- var current = current
-
var buf = new FlatBuffer
- if result != null and (current == null or last_op != '=') then
- if last_op == '=' then buf.append "= "
+ var last_op = last_op
+ var result = result
+ if result != null then
+ if last_op_was_unary then buf.append "{last_op or else "?"} "
buf.append result.to_s
buf.add ' '
end
- if last_op != null and last_op != '=' then
- buf.add last_op
+ if last_op != null and not last_op_was_unary then
+ buf.append last_op
buf.add ' '
end
+ var current = current
if current != null then
buf.append current.to_s
buf.add ' '
end
# Push operation `op`, will usually execute the last operation
- fun push_op(op: Char)
+ fun push_op(op: Text)
do
+ # Constants
+ # TODO Protect constants to preserve full precision and to forbid appending extra digits
+ if op == "π" then
+ if last_op_was_unary then clear
+ current = pi.to_s
+ return
+ else if op == "e" then
+ if last_op_was_unary then clear
+ current = 2.718282.to_s
+ return
+
+ # Clear screen
+ else if op == "C" then
+ clear
+ return
+
+ # Unary -
+ else if op == "-" then
+ if current == null then
+ if last_op_was_unary then clear
+ current = "-"
+ return
+ else if current == "-" then
+ current = null
+ return
+ end
+ end
+
+ # For all operators, apply pending operators
apply_last_op_if_any
- if op == 'C' then
- self.result = null
- last_op = null
+
+ var result = self.result or else 0
+
+ last_op = op
+ last_op_was_unary = true
+
+ # Unary operators
+ if op == "√" then
+ self.result = result.to_f.sqrt
+ else if op == "x²" then
+ self.result = result.to_f.pow(2.0)
+ else if op == "x!" then
+ self.result = result.to_i.factorial
+ else if op == "sin" then
+ self.result = result.to_f.sin
+ else if op == "cos" then
+ self.result = result.to_f.cos
+ else if op == "tan" then
+ self.result = result.to_f.tan
+
+ # =
+ else if op == "=" then
+ current = null
+
+ # Binary operators
else
- last_op = op # store for next push_op
+ self.result = result # Set as same or 0
+ last_op_was_unary = false
+ current = null
end
+ end
- # prepare next current
- self.current = null
+ # Clear all state
+ private fun clear
+ do
+ result = null
+ last_op = null
+ last_op_was_unary = false
+ current = null
end
# Push a digit
fun push_digit(digit: Int)
do
+ if last_op_was_unary then clear
+
var current = current
- if current == null then current = new FlatBuffer
- current.add digit.to_s.chars.first
+ if current == null then current = ""
+ current += digit.to_s
self.current = current
-
- if last_op == '=' then
- self.result = null
- last_op = null
- end
end
# Switch entry mode from integer to decimal
fun switch_to_decimals
do
var current = current
- if current == null then current = new FlatBuffer.from("0")
- if not current.chars.has('.') then current.add '.'
+ if current == null then current = "0"
+ if not current.chars.has('.') then current += "."
self.current = current
end
protected fun apply_last_op_if_any
do
var op = last_op
-
var result = result
-
var current = current
- if current == null then current = new FlatBuffer
+ self.current = null
+
+ if current == null then return
if op == null then
result = current.to_n
- else if op == '+' then
- result = result.add(current.to_n)
- else if op == '-' then
- result = result.sub(current.to_n)
- else if op == '/' then
- result = result.div(current.to_n)
- else if op == '*' then
- result = result.mul(current.to_n)
+ else if result != null then
+ if op == "+" then
+ result = result.add(current.to_n)
+ else if op == "-" then
+ result = result.sub(current.to_n)
+ else if op == "/" or op == "÷" then
+ result = result.div(current.to_n)
+ else if op == "*" or op == "×" then
+ result = result.mul(current.to_n)
+ else if op == "%" then
+ result = result.to_i % current.to_i
+ else if op == "xⁿ" then
+ result = result.to_f.pow(current.to_f)
+ else if op == "log" then
+ result = result.to_f.log_base(current.to_f)
+ end
end
self.result = result
- self.current = null
end
end