Packages

The Phi Programming Language

Current section

Files

Jump to
phi lib System IO Printf.hm
Raw

lib/System/IO/Printf.hm

-----------------------------------------------------------------------------
-- |
-- Module : System.IO.Printf
-- Copyright : (c) 2020-2021 EMQ Technologies Co., Ltd.
-- License : BSD-style (see the LICENSE file)
--
-- Maintainer : Feng Lee, feng@emqx.io
-- Yang M, yangm@emqx.io
-- Stability : experimental
-- Portability : portable
--
-- The System Printf module.
--
-----------------------------------------------------------------------------
module System.IO.Printf
( Control
, class Printf
, class PrintArg
, print
, printf
, println
, render
) where
{-
`printf` in Hamler is defined like in Haskell. It has type `Printf r => String -> r`. The String argument represent how the format of the output, just like printfs in ohter languages.
exmaple 1:
```haskell
$
> printf "%d is one" 1
1 is one
```
The Following list are format characters, specify the type and the output form.
c character
d decimal
o octal
x hexadecimal
b binary
f floating point
s string
In our specification we can have flags to limit the length and precision of the output.
- left adjust (default is right adjust)
+ right adjust
after flag there will be number specify field length, which is a mininum not maximum.
we can also have precision with after field lenght
.num precision
. same as .0
example 2:
```haskell
$
> printf "%s %10.2f" "pi" pi
pi 3.14
```
exmaple 3:
```haskell
$
> printf "%-10.2f %s" pi "pi"
3.14 pi
```
-}
import Control.Monad (IO)
import Data.Bool (not)
import Data.Eq ((/=))
import Data.Float (float, pow, round)
import Data.Function (($), error)
import Data.List (init, last, length, member, replicate, span, (++))
import Data.Maybe (Maybe(..))
import Data.Ord ((<))
import Data.Ring ((%), (-), (/))
import Data.Semigroup ((<>))
import Data.Semiring ((*))
import Data.Show (showAny,show)
import Data.String (toInteger)
import Data.Unit (Unit)
-- Simple IO operations
foreign import print :: String -> IO ()
foreign import println :: String -> IO ()
foreign import println_ :: forall a. String -> IO a
data Dir = L | R
data Control = Control (Maybe Dir) (Maybe Integer) (Maybe Integer) Char
flagCharactor :: String
flagCharactor = "+-"
formatCharacter :: String
formatCharacter = "cdoxXbufFgGeEs"
myspan :: String -> String -> (String, String)
myspan s ss = span (\c -> member c s) ss
charToDir :: Char -> Dir
charToDir '+' = L
charToDir '-' = R
charToDir _ = error "error input"
parse :: String -> Control
parse ss = let vlast = last ss
initls = init ss
(v1,res) = case myspan flagCharactor initls of
([], xs) -> (Nothing,xs)
([x], xs) -> (Just $ charToDir x, xs)
(x,_) -> error "input error"
(v2,v3) = case span (\c -> c /= '.') res of
([],[]) -> (Nothing, Nothing)
(a,[]) -> (Just $ toInteger a, Nothing)
([],[_|b]) -> (Nothing, Just $ toInteger b)
(a,[_|b]) -> (Just $ toInteger a, Just $ toInteger b)
in Control v1 v2 v3 vlast
class Printf t where
printf :: String -> t
instance Printf (IO a) where
printf s = println_ s
instance (PrintArg a, Printf t) => Printf (a -> t) where
printf s = \a -> printf (format s a)
class PrintArg a where
render :: Control -> a -> String
partcal' :: Integer -> Integer -> String
partcal' _ 0 = ""
partcal' k x = let (a,b) = (x/k,x%k)
in partcal' k a <> rhex b
partcal :: Integer -> Integer -> String
partcal _ 0 = "0"
partcal k x = partcal' k x
rstr :: Integer -> String
rstr 2 = "0B"
rstr 8 = "0O"
rstr 10 = ""
rstr 16 = "0X"
rstr _ = error "error input"
crestr :: Integer -> Integer -> String
crestr k x = rstr k <> partcal k x
rhex :: Integer -> String
rhex 10 = "A"
rhex 11 = "B"
rhex 12 = "C"
rhex 13 = "D"
rhex 14 = "E"
rhex 15 = "F"
rhex x = show x
instance PrintArg Integer where
render (Control a b c d) i =
let si = case d of
'b' -> crestr 2 i
'o' -> crestr 8 i
'd' -> crestr 10 i
'x' -> crestr 16 i
_ -> error "error input"
slen = length si
s1 = case c of
Nothing -> si
Just v -> if v < slen
then si
else replicate (v - slen) '0' ++ si
in mutilP a b s1
instance PrintArg Float where
render (Control a b c d) i =
let s1 = case c of
Nothing -> show i
Just v -> let t = pow 10.0 (float v)
in show $ round (i * t) / t
in mutilP a b s1
instance PrintArg [Char] where
render (Control a b c 's') s = mutilP a b s
render o _ = error $ "String don't accept the control symbal: " ++ showAny o
mutilP :: (Maybe Dir) -> Maybe Integer -> String -> String
mutilP a b s = case b of
Nothing -> s
Just v -> if v < length s
then s
else case a of
Just R -> s ++ replicate (v - length s) ' '
_ -> replicate (v - length s) ' ' ++ s
format :: forall a.PrintArg a => String -> a -> String
format ['%'|xs] a = case span (\c -> not $ member c formatCharacter) xs of
(ctr, [c|res]) -> render (parse $ ctr ++ [c]) a ++ res
_ -> error "bad format"
format [x|xs] a = [x| format xs a]
format [] _ =[]