-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpipemath.hs
68 lines (60 loc) · 2.52 KB
/
pipemath.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import Text.Read (readMaybe)
import System.Environment (getArgs)
import System.IO (hPutStrLn, stderr)
import System.Exit (exitFailure)
main :: IO ()
main = do
args <- getArgs
case args of
[] -> do
putStr "missing arg"
[equationString] -> do
stdin <- getContents
let results = main' equationString stdin
case results of
Left error -> failProgram error
Right results' -> mapM_ print results'
_ -> failProgram "Too many arguments given"
main' :: String -> String -> Either String [Float]
main' equationString stdin = do
eqn <- parseEquation equationString
nums <- parseLines . lines $ stdin
Right $ eqn nums
failProgram :: String -> IO ()
failProgram s = hPutStrLn stderr s >> exitFailure
type Eqn = ([Float] -> [Float])
-- TODO could support piping within the given string... e.g. "*2 | avg"
parseEquation :: String -> Either String Eqn
parseEquation text = go $ tokenize text
where
go :: [String] -> Either String Eqn
-- Arithmetic
go ["*", n] = Right (map (* (read n :: Float)))
go [n, "*"] = Right (map (* (read n :: Float)))
go ["+", n] = Right (map (+ (read n :: Float)))
go [n, "+"] = Right (map (+ (read n :: Float)))
go ["/", n] = Right (map (\x -> x / (read n :: Float)))
go [n, "/"] = Right (map (\x -> (read n :: Float) / x))
go ["-", n] = Right (map (\x -> x - (read n :: Float)))
go [n, "-"] = Right (map (\x -> (read n :: Float) - x))
-- Aggregate
go ["sum"] = Right $ \x -> [ sum x ]
go ["avg"] = Right $ \x -> [ sum x / fromIntegral (length x) ]
-- TODO add median?
-- Rounding
go ["round"] = Right (map $ fromIntegral . round)
go ["floor"] = Right (map $ fromIntegral . floor)
go ["ceil"] = Right (map $ fromIntegral . ceiling)
go ["ceiling"] = Right (map $ fromIntegral . ceiling)
go _ = Left $ "Error: Could not parse equation: '" ++ text ++ "'"
-- TODO support things like "+2" or "8-" without a space btwn number and operator
tokenize :: String -> [String]
tokenize = words
parseLines :: [String] -> Either String [Float]
parseLines = mapM parseLine . zip [1..]
where
parseLine :: (Int, String) -> Either String Float
parseLine (i, line) = let parsed = readMaybe line
in case parsed of
Nothing -> Left $ "Error: line " ++ show i ++ ": Could not parse as number: " ++ line
Just x -> Right x