{- |
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
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Operator -> Operator -> Bool
$c/= :: Operator -> Operator -> Bool
== :: Operator -> Operator -> Bool
$c== :: Operator -> Operator -> Bool
Eq, Int -> Operator
Operator -> Int
Operator -> [Operator]
Operator -> Operator
Operator -> Operator -> [Operator]
Operator -> Operator -> Operator -> [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
enumFromThenTo :: Operator -> Operator -> Operator -> [Operator]
$cenumFromThenTo :: Operator -> Operator -> Operator -> [Operator]
enumFromTo :: Operator -> Operator -> [Operator]
$cenumFromTo :: Operator -> Operator -> [Operator]
enumFromThen :: Operator -> Operator -> [Operator]
$cenumFromThen :: Operator -> Operator -> [Operator]
enumFrom :: Operator -> [Operator]
$cenumFrom :: Operator -> [Operator]
fromEnum :: Operator -> Int
$cfromEnum :: Operator -> Int
toEnum :: Int -> Operator
$ctoEnum :: Int -> Operator
pred :: Operator -> Operator
$cpred :: Operator -> Operator
succ :: Operator -> Operator
$csucc :: Operator -> Operator
Enum, Operator
forall a. a -> a -> Bounded a
maxBound :: Operator
$cmaxBound :: Operator
minBound :: Operator
$cminBound :: Operator
Bounded, Int -> Operator -> ShowS
[Operator] -> ShowS
Operator -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Operator] -> ShowS
$cshowList :: [Operator] -> ShowS
show :: Operator -> String
$cshow :: Operator -> String
showsPrec :: Int -> Operator -> ShowS
$cshowsPrec :: Int -> 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
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Element -> Element -> Bool
$c/= :: Element -> Element -> Bool
== :: Element -> Element -> Bool
$c== :: Element -> Element -> Bool
Eq, Int -> Element -> ShowS
[Element] -> ShowS
Element -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Element] -> ShowS
$cshowList :: [Element] -> ShowS
show :: Element -> String
$cshow :: Element -> String
showsPrec :: Int -> Element -> ShowS
$cshowsPrec :: Int -> 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 = forall a b. (a -> b) -> [a] -> [b]
map String -> Element
parseToken forall a b. (a -> b) -> a -> b
$ String -> [String]
words String
expr

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