-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParserLib.hs
186 lines (159 loc) · 5.55 KB
/
ParserLib.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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
-- | A small, applicative-based parsing library
-- NOTE: this library does not export the `P` data constructor.
-- All `Parser`s must be built using the following functions
-- exported by this file, as well as the `Functor`, `Applicative` and
-- `Alternative` operations.
module ParserLib
( Parser,
doParse,
get,
eof,
filter,
parse,
parseFromFile,
ParseError,
satisfy,
alpha,
digit,
upper,
lower,
space,
char,
string,
int,
chainl1,
chainl,
choice,
between,
sepBy1,
sepBy,
)
where
import Control.Applicative (Alternative (..))
import Control.Monad (guard)
import Data.Char
import Data.Foldable (asum)
import System.IO qualified as IO
import System.IO.Error qualified as IO
import Text.Read (readMaybe)
import Prelude hiding (filter)
-- definition of the parser type
newtype Parser a = P {doParse :: String -> Maybe (a, String)}
instance Functor Parser where
fmap :: (a -> b) -> Parser a -> Parser b
fmap f p = P $ \s -> do
(c, cs) <- doParse p s
return (f c, cs)
instance Applicative Parser where
pure :: a -> Parser a
pure x = P $ \s -> Just (x, s)
(<*>) :: Parser (a -> b) -> Parser a -> Parser b
p1 <*> p2 = P $ \s -> do
(f, s') <- doParse p1 s
(x, s'') <- doParse p2 s'
return (f x, s'')
instance Alternative Parser where
empty :: Parser a
empty = P $ const Nothing
(<|>) :: Parser a -> Parser a -> Parser a
p1 <|> p2 = P $ \s -> doParse p1 s `firstJust` doParse p2 s
-- | Combine two Maybe values together, producing the first
-- successful result
firstJust :: Maybe a -> Maybe a -> Maybe a
firstJust (Just x) _ = Just x
firstJust Nothing y = y
-- | Return the next character from the input
get :: Parser Char
get = P $ \s -> case s of
(c : cs) -> Just (c, cs)
[] -> Nothing
-- | This parser *only* succeeds at the end of the input.
eof :: Parser ()
eof = P $ \s -> case s of
[] -> Just ((), [])
_ : _ -> Nothing
-- | Filter the parsing results by a predicate
filter :: (a -> Bool) -> Parser a -> Parser a
filter f p = P $ \s -> do
(c, cs) <- doParse p s
guard (f c)
return (c, cs)
---------------------------------------------------------------
---------------------------------------------------------------
---------------------------------------------------------------
type ParseError = String
-- | Use a parser for a particular string. Note that this parser
-- combinator library doesn't support descriptive parse errors, but we
-- give it a type similar to other Parsing libraries.
parse :: Parser a -> String -> Either ParseError a
parse parser str = case doParse parser str of
Nothing -> Left "No parses"
Just (a, _) -> Right a
-- | parseFromFile p filePath runs a string parser p on the input
-- read from filePath using readFile. Returns either a
-- ParseError (Left) or a value of type a (Right).
parseFromFile :: Parser a -> String -> IO (Either ParseError a)
parseFromFile parser filename = do
IO.catchIOError
( do
handle <- IO.openFile filename IO.ReadMode
str <- IO.hGetContents handle
pure $ parse parser str
)
( \e ->
pure $ Left $ "Error:" ++ show e
)
-- | Return the next character if it satisfies the given predicate
satisfy :: (Char -> Bool) -> Parser Char
satisfy p = filter p get
-- | Parsers for specific sorts of characters
alpha, digit, upper, lower, space :: Parser Char
alpha = satisfy isAlpha
digit = satisfy isDigit
upper = satisfy isUpper
lower = satisfy isLower
space = satisfy isSpace
-- | Parses and returns the specified character
-- succeeds only if the input is exactly that character
char :: Char -> Parser Char
char c = satisfy (c ==)
-- | Parses and returns the specified string.
-- Succeeds only if the input is the given string
string :: String -> Parser String
string = foldr (\c p -> (:) <$> char c <*> p) (pure "")
-- | succeed only if the input is a (positive or negative) integer
int :: Parser Int
int = f <$> ((++) <$> string "-" <*> some digit <|> some digit)
where
f str = case readMaybe str of
Just x -> x
Nothing -> error $ "Bug: can't parse '" ++ str ++ "' as an int"
-- | Parses one or more occurrences of @p@ separated by binary operator
-- parser @pop@. Returns a value produced by a /left/ associative application
-- of all functions returned by @pop@.
-- See the end of the `Parsers` lecture for explanation of this operator.
chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a
p `chainl1` pop = foldl comb <$> p <*> rest
where
comb x (op, y) = x `op` y
rest = many ((,) <$> pop <*> p)
-- | @chainl p pop x@ parses zero or more occurrences of @p@, separated by @pop@.
-- If there are no occurrences of @p@, then @x@ is returned.
chainl :: Parser b -> Parser (b -> b -> b) -> b -> Parser b
chainl p pop x = chainl1 p pop <|> pure x
-- | Combine all parsers in the list (sequentially)
choice :: [Parser a] -> Parser a
choice = asum -- equivalent to: foldr (<|>) empty
-- | @between open close p@ parses @open@, followed by @p@ and finally
-- @close@. Only the value of @p@ is pureed.
between :: Parser open -> Parser a -> Parser close -> Parser a
between open p close = open *> p <* close
-- | @sepBy p sep@ parses zero or more occurrences of @p@, separated by @sep@.
-- Returns a list of values returned by @p@.
sepBy :: Parser a -> Parser sep -> Parser [a]
sepBy p sep = sepBy1 p sep <|> pure []
-- | @sepBy1 p sep@ parses one or more occurrences of @p@, separated by @sep@.
-- Returns a list of values returned by @p@.
sepBy1 :: Parser a -> Parser sep -> Parser [a]
sepBy1 p sep = (:) <$> p <*> many (sep *> p)
---------------------------------------------