Merge: Functional api
[nit.git] / examples / calculator / src / calculator_logic.nit
index 9666d14..765f37a 100644 (file)
@@ -1,7 +1,5 @@
 # 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