packages feed

clac-0.3.0: src-exec/clac.hs

{-# LANGUAGE GADTs              #-}
{-# LANGUAGE RankNTypes         #-}
{-# LANGUAGE StandaloneDeriving #-}

{- |
Module     : $Header$
Description: clac.
Copyright  : (c) Alexander Berntsen 2015
License    : GPL-3

Maintainer : alexander@plaimi.net
-} module Main where

import Control.Applicative
  (
  (<$>),
  (<|>),
  (<*>),
  )
import Control.Arrow
  (
  second,
  )
import Control.Monad
  (
  when,
  )
import Data.List
  (
  find,
  )
import Data.List.Split
  (
  splitOn,
  )
import Data.Monoid
  (
  (<>),
  mempty,
  )
import Data.Tree
  (
  Tree (Node),
  )
import Data.Tree.Pretty
  (
  drawVerticalTree,
  )
import Options.Applicative
  (
  Parser,
  execParser,
  fullDesc,
  header,
  help,
  helper,
  info,
  long,
  many,
  progDesc,
  short,
  strArgument,
  switch,
  )
import Safe
  (
  readMay,
  )


data Opt = MkOpt {h :: Bool
                 ,v :: Bool
                 ,e :: [String]
                 }

data StackItem a where
  Snum :: forall a. Fractional a => a -> StackItem a
  Sop  :: OpDesc -> StackItem a
deriving instance Show a => Show (StackItem a)

data OpDesc = Dop {op   :: Op
                  ,desc :: String
                  }
instance Show OpDesc where
  show (Dop _ a) = a

data Op where
  Bop :: (forall a. Fractional a => a -> a -> a) -> Op
  Uop :: (forall a. Floating a => a -> a) -> Op
  C   :: (forall a. Floating a => a) -> Op
  Neq :: Op

ops :: Parser Opt
ops = MkOpt
  <$> switch
      ( long "operations"
     <> short 'o'
     <> help "Print all operations" )
  <*> switch
      ( long "verbose"
     <> short 'v'
     <> help "Verbose output" )
  <*> many (strArgument mempty)

os :: [(OpDesc, String)]
os = [( Dop (Bop (+))    "+",   "+:\t\taddition"                 )
     ,( Dop (Bop (-))    "-",   "-:\t\tsubtraction"              )
     ,( Dop (Bop (*))    "*",   "*:\t\tmultiplication"           )
     ,( Dop (Bop (*))    "x",   "*:\t\tmultiplication"           )
     ,( Dop (Bop (/))    "/",   "/:\t\tdivision"                 )
     ,( Dop (Uop negate) "neg", "neg:\t\tnegation"               )
     ,( Dop (Uop sin)    "sin", "sin:\t\tsine function"          )
     ,( Dop (Uop cos)    "cos", "cos:\t\tcosine function"        )
     ,( Dop (Uop tan)    "tan", "tan:\t\ttangent function"       )
     ,( Dop (Uop asin)   "asin","asine:\t\tarcsine function"     )
     ,( Dop (Uop acos)   "acos","acosine:\tarccosine function"   )
     ,( Dop (Uop atan)   "atan","arctan:\t\tarctangent function" )
     ,( Dop (C   pi)     "pi",  "pi:\t\tpi constant"             )
     ,( Dop Neq          ",",   ",:\t\tstart a new equation"     )
     ]

b :: String -> [StackItem Double] -> [StackItem Double]
b x ac = case p x of
           Just q  -> q:ac
           Nothing -> ac

p :: String -> Maybe (StackItem Double)
p m = (Sop <$> find ((== m) . desc) (fst <$> os))
  <|> Snum <$> (readMay m :: Maybe Double)

t :: [StackItem Double] -> Tree String
t (Sop (Dop (Bop _) i):j:k) = Node i [t k, t [j]]
t (Sop (Dop (Uop _) i):j)   = Node i [t j]
t (Sop (Dop (C _  ) i):_)   = Node i []
t (Snum i:_)                = Node (show i) []
t _                         = Node "¯\\_(ツ)_/¯" []

s :: [StackItem Double] -> [StackItem Double] -> Maybe Double
s (Sop (Dop (Bop o) _):ss) (Snum n:Snum m:ts) = s ss (Snum (m `o` n):ts)
s (Sop (Dop (Uop o) _):ss) (Snum m:ts)        = s ss (Snum (o m):ts)
s (Sop (Dop (C   c) _):ss) ts                 = s ss (Snum c:ts)
s (n:ss) ts                                   = s ss (n:ts)
s [] (Snum n:_)                               = Just n
s _ _                                         = Nothing

sa :: [[String]] -> [(Maybe Double, String)]
sa = map $ (second drawVerticalTree . (((,) . flip s [])
 <*> (t . reverse))) . foldr b []

calc :: Opt -> IO ()
calc o = do
  cs <- if null (e o) then getContents else return []
  let es = splitOn [","] $ words cs ++ case e o of
                                         [a] -> words a
                                         _   -> e o
  if h o
    then mapM_ putStrLn $ "OPERATORS":"=========":map snd os
    else
      mapM_ (\(solution, tree) -> do
        when (v o) $ putStrLn $ "\n\n" ++ tree
        print solution
        putStrLn $ replicate (length $ show solution) '=') $ sa es

main :: IO ()
main = execParser o >>= calc
  where o =
          info (helper <*> ops)
          ( fullDesc
         <> progDesc "simple CLI RPN calculator"
         <> header   "clac" )