aboutsummaryrefslogtreecommitdiff
path: root/src/parser/Core.hs
blob: 5928fe48c14c0b76983a84d976d7b5ffeaa528ea (plain)
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
{-# LANGUAGE FlexibleInstances #-}

module Parser.Core where

import           Control.Applicative
import           Data.Char


newtype Parser a = Parser { runParser :: String -> Either String (a, String) }

runParserStrict :: Parser a -> String -> Either String a
runParserStrict p input = case runParser p input of
    Right (a, "")   -> Right a
    Right (_, rest) -> Left $ "Unconsumed input: \"" ++ rest ++ "\""
    Left err        -> Left err

-------------------------------------------------------------------------------
-- Parser instances
-------------------------------------------------------------------------------

instance Functor Parser where
    -- fmap :: (a -> b) -> Parser a -> Parser b
    fmap f (Parser p) = Parser $
        \s -> do (x, s') <- p s
                 return (f x, s')

instance Applicative Parser where
    -- pure :: a -> Parser a
    pure x = Parser (\s -> Right (x, s))
    -- (<*>) :: Parser (a -> b) -> Parser a -> Parser b
    (Parser pf) <*> (Parser p) = Parser $
        \s -> do (f, s')  <- pf s
                 (x, s'') <- p s'
                 return (f x, s'')

instance Monad Parser where
    -- return :: a -> Parser a
    return x = pure x
    -- (>>=) :: Parser a -> (a -> Parser b) -> Parser b
    (Parser p1) >>= f = Parser $
        \s -> do (x, s') <- p1 s
                 runParser (f x) s'

-- instance for Either String so that it can be used in the Alternative for Parser
instance Alternative (Either String) where
    -- empty :: Either String a
    empty = Left ""
    -- (<|>) :: Either String a -> Either String a -> Either String a
    Left _ <|> x2 = x2
    x1     <|> _  = x1

instance Alternative Parser where
    -- empty :: Parser a
    empty = Parser (\_ -> Left "Empty")
    -- (<|>) :: Parser a -> Parser a -> Parser a
    (Parser p1) <|> (Parser p2) = Parser $ \s -> p1 s <|> p2 s


-------------------------------------------------------------------------------
-- Parser creation helper
-------------------------------------------------------------------------------

-- Create a parser of one character which must respect a predicate
satisfyChar :: (Char -> Bool) -> Parser Char
satisfyChar predicate = Parser p
    where p []     = Left "Expected input"
          p (c:cs) = if predicate c then Right (c, cs)
                                    else Left $ "Unexpected char '" ++ [c] ++ "'"

char :: Char -> Parser Char
char c = satisfyChar (c ==)

string :: String -> Parser String
string s = sequenceA $ char <$> s

sepBy :: Parser a -> Parser b -> Parser [a]
sepBy x sepatator = (:) <$> x <*> (many (sepatator *> x))

-- Parse one or more occurences of p separated by op
-- Apply op in a left associative maner on each value in p
chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a
chainl1 p op = p >>= rest
    where rest prev = do f <- op
                         operand <- p
                         rest (f prev operand)
                      <|> return prev

infixOp :: String -> (a -> a -> a) -> Parser (a -> a -> a)
infixOp operatorStr f = string operatorStr *> pure f

-- Surround parser with opening and closing string
between :: String -> String -> Parser a -> Parser a
between open close p = string open *> p <* string close

parenthesis :: Parser a -> Parser a
parenthesis p = between "(" ")" p

-- try to apply parsers returns the first one that succeeds
choice :: [Parser a] -> Parser a
choice []     = empty
choice (p:ps) = p <|> choice ps

-- verify :: (a -> Bool) -> Parser a -> Parser a
-- verify predicate p = do a <- p
--                         if predicate a then p else Parser (\_ -> Left "Bonjour")

-- Parse a string of alpha character, converted to lower case
labelP :: Parser String
labelP = (map toLower) <$> some (satisfyChar isAlpha)


floatP :: Parser Float
floatP = signed unsignedP
    where
        unsignedP :: Parser Float
        unsignedP = read <$> p
            where p = do pos <- digitsP
                         _ <- char '.'
                         dec <- digitsP
                         return (pos ++ "." ++ dec)
                      <|> digitsP

                  digitsP = some $ satisfyChar isDigit -- at least one digit to avoid read exception

        signed :: Num a => Parser a -> Parser a
        signed p = do _ <- char '-'
                      x <- p
                      return (-x)
                     <|> p