{- |
Description: Supporting definitions for monad problems
Copyright: Copyright (C) 2023 Yoo Chung
License: GPL-3.0-or-later
Maintainer: dev@chungyc.org

Supporting definitions for monad problems.
-}
module Problems.Monads (Operator (..), Element (..), parsePostfix) where

import           Data.Char (isDigit)

-- | Encodes an operator for a mathematical expression.
data Operator
  -- | Encodes negation.  Equivalent to an unary minus.  Unary operator.
  = Negate
  -- | Encodes duplication.  Makes another copy of its operand.  Unary operator.
  | Add
  -- | Encodes subtraction.  Binary operator.
  | Subtract
  -- | Encodes multiplication.  Binary operator.
  | Multiply
  -- | Encodes division.  Equivalent to 'div'.  Binary operator.
  | Divide
  -- | Encodes a modulo operator.  Equivalent to 'mod'.  Binary operator.
  | Modulo
  deriving (Operator -> Operator -> Bool
(Operator -> Operator -> Bool)
-> (Operator -> Operator -> Bool) -> Eq Operator
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Operator -> Operator -> Bool
== :: Operator -> Operator -> Bool
$c/= :: Operator -> Operator -> Bool
/= :: Operator -> Operator -> Bool
Eq, Int -> Operator
Operator -> Int
Operator -> [Operator]
Operator -> Operator
Operator -> Operator -> [Operator]
Operator -> Operator -> Operator -> [Operator]
(Operator -> Operator)
-> (Operator -> Operator)
-> (Int -> Operator)
-> (Operator -> Int)
-> (Operator -> [Operator])
-> (Operator -> Operator -> [Operator])
-> (Operator -> Operator -> [Operator])
-> (Operator -> Operator -> Operator -> [Operator])
-> Enum Operator
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: Operator -> Operator
succ :: Operator -> Operator
$cpred :: Operator -> Operator
pred :: Operator -> Operator
$ctoEnum :: Int -> Operator
toEnum :: Int -> Operator
$cfromEnum :: Operator -> Int
fromEnum :: Operator -> Int
$cenumFrom :: Operator -> [Operator]
enumFrom :: Operator -> [Operator]
$cenumFromThen :: Operator -> Operator -> [Operator]
enumFromThen :: Operator -> Operator -> [Operator]
$cenumFromTo :: Operator -> Operator -> [Operator]
enumFromTo :: Operator -> Operator -> [Operator]
$cenumFromThenTo :: Operator -> Operator -> Operator -> [Operator]
enumFromThenTo :: Operator -> Operator -> Operator -> [Operator]
Enum, Operator
Operator -> Operator -> Bounded Operator
forall a. a -> a -> Bounded a
$cminBound :: Operator
minBound :: Operator
$cmaxBound :: Operator
maxBound :: Operator
Bounded, Int -> Operator -> ShowS
[Operator] -> ShowS
Operator -> String
(Int -> Operator -> ShowS)
-> (Operator -> String) -> ([Operator] -> ShowS) -> Show Operator
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Operator -> ShowS
showsPrec :: Int -> Operator -> ShowS
$cshow :: Operator -> String
show :: Operator -> String
$cshowList :: [Operator] -> ShowS
showList :: [Operator] -> ShowS
Show)

-- | A single element within a mathematical expression.
-- A list of these elements comprises an expression in postfix notation.
data Element = Operator Operator | Operand Integer deriving (Element -> Element -> Bool
(Element -> Element -> Bool)
-> (Element -> Element -> Bool) -> Eq Element
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Element -> Element -> Bool
== :: Element -> Element -> Bool
$c/= :: Element -> Element -> Bool
/= :: Element -> Element -> Bool
Eq, Int -> Element -> ShowS
[Element] -> ShowS
Element -> String
(Int -> Element -> ShowS)
-> (Element -> String) -> ([Element] -> ShowS) -> Show Element
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Element -> ShowS
showsPrec :: Int -> Element -> ShowS
$cshow :: Element -> String
show :: Element -> String
$cshowList :: [Element] -> ShowS
showList :: [Element] -> ShowS
Show)

-- | Parses a string containing a mathematical expression in postfix notation.
-- This can make it easier to write down an expression in a more conventional form.
--
-- For example,
--
-- >>> parsePostfix "3 4 2 - *"
-- [Operand 3,Operand 4,Operand 2,Operator Subtract,Operator Multiply]
--
-- The operators are encoded as follows:
--
-- +-------------+------------------+
-- | Operator    | String           |
-- +=============+==================+
-- | 'Negate'    | @"negate"@       |
-- +-------------+------------------+
-- | 'Add'       | @"+"@            |
-- +-------------+------------------+
-- | 'Subtract'  | @"-"@            |
-- +-------------+------------------+
-- | 'Multiply'  | @"*"@            |
-- +-------------+------------------+
-- | 'Divide'    | @"/"@            |
-- +-------------+------------------+
-- | 'Modulo'    | @"%"@            |
-- +-------------+------------------+
parsePostfix :: String -> [Element]
parsePostfix :: String -> [Element]
parsePostfix String
expr = (String -> Element) -> [String] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map String -> Element
parseToken ([String] -> [Element]) -> [String] -> [Element]
forall a b. (a -> b) -> a -> b
$ String -> [String]
words String
expr

parseToken :: String -> Element
parseToken :: String -> Element
parseToken String
x
  | (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
x = Integer -> Element
Operand (String -> Integer
forall a. Read a => String -> a
read String
x)
  | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"negate" = Operator -> Element
Operator Operator
Negate
  | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"+" = Operator -> Element
Operator Operator
Add
  | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"-" = Operator -> Element
Operator Operator
Subtract
  | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"*" = Operator -> Element
Operator Operator
Multiply
  | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"/" = Operator -> Element
Operator Operator
Divide
  | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"%" = Operator -> Element
Operator Operator
Modulo
  | Bool
otherwise = Element
forall a. HasCallStack => a
undefined