# This file is part of NIT ( http://www.nitlanguage.org ).
#
-# Copyright 2013-2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Business logic of a calculator
module calculator_logic
+import serialization
+
+# Hold the state of the calculator and its services
class CalculatorContext
- var result : nullable Float = null
+ auto_serializable
+
+ # Result of the last operation
+ var result: nullable Numeric = null
- var last_op : nullable Char = null
+ # Last operation pushed with `push_op`
+ var last_op: nullable Text = null
- var current : nullable Float = null
- var after_point : nullable Int = null
+ # Is `last_op` an unary operation or a '='?
+ var last_op_was_unary = false
- fun push_op( op : Char )
+ # Value currently being entered
+ var current: nullable String = null
+
+ # Text to display on screen
+ fun display_text: String
do
- apply_last_op_if_any
- if op == 'C' then
- self.result = 0.0
- last_op = null
- else
- last_op = op # store for next push_op
+ var buf = new FlatBuffer
+
+ 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
- # prepare next current
- after_point = null
- current = null
+ 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
+
+ return buf.to_s
end
- fun push_digit( digit : Int )
+ # Push operation `op`, will usually execute the last operation
+ fun push_op(op: Text)
do
- var current = current
- if current == null then current = 0.0
+ # 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
+
+ var result = self.result or else 0
- var after_point = after_point
- if after_point == null then
- current = current * 10.0 + digit.to_f
+ 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
- current = current + digit.to_f * 10.0.pow(after_point.to_f)
- self.after_point -= 1
+ self.result = result # Set as same or 0
+ last_op_was_unary = false
+ current = null
end
+ end
+ # 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 = ""
+ current += digit.to_s
self.current = current
end
+ # Switch entry mode from integer to decimal
fun switch_to_decimals
do
- if self.current == null then current = 0.0
- if after_point != null then return
-
- after_point = -1
+ var current = current
+ if current == null then current = "0"
+ if not current.chars.has('.') then current += "."
+ self.current = current
end
- fun apply_last_op_if_any
+ # Execute the last operation it not null
+ protected fun apply_last_op_if_any
do
var op = last_op
-
var result = result
- if result == null then result = 0.0
-
var current = current
- if current == null then current = 0.0
+ self.current = null
+
+ if current == null then return
if op == null then
- result = current
- else if op == '+' then
- result = result + current
- else if op == '-' then
- result = result - current
- else if op == '/' then
- result = result / current
- else if op == '*' then
- result = result * current
+ result = 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
+
+redef universal Float
+ redef fun to_s do return to_precision(6)
+end