forked from wangbj/introhs-demo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.hs
173 lines (139 loc) · 4.06 KB
/
Parser.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
module Parser
(
Parser (..)
, Input
, ParseError (..)
, ParseResult (..)
, isErrorResult
, unexpectedCharParser
, valueParser
, failed
, anyChar
, satisfy
, digit
, char
, string
, choice
, eof
, count
) where
import Control.Applicative
import Control.Monad
import Data.Char
type Input = String
data ParseError = UnexpectedEof
| ExpectedEof Input
| UnexpectedChar Char
| Failed
deriving Eq
instance Show ParseError where
show UnexpectedEof =
"Unexpected end of stream"
show (ExpectedEof i) =
concat ["Expected end of stream, but got >", show i, "<"]
show (UnexpectedChar c) =
concat ["Unexpected character: ", show [c]]
show Failed = "Parse failed"
data ParseResult a = ErrorResult ParseError
| Result Input a
deriving Eq
isErrorResult :: ParseResult a -> Bool
isErrorResult (ErrorResult _) = True
isErrorResult (Result _ _) = False
-- ^A parser consumes ``Input``, parse it to a result as well as
-- remaining input, or return a error on failed parsing.
newtype Parser a = P {
parse :: Input -> ParseResult a
}
unexpectedCharParser :: Char -> Parser a
unexpectedCharParser c = P $ \_ -> ErrorResult (UnexpectedChar c)
valueParser :: a -> Parser a
valueParser x = P (\i -> Result i x)
failed :: Parser a
failed = P (\_ -> ErrorResult Failed)
anyChar :: Parser Char
anyChar = P $ \s -> case s of
[] -> ErrorResult UnexpectedEof
(c:cs) -> Result cs c
many1 :: Parser a -> Parser [a]
many1 = some
satisfy :: (Char -> Bool)
-> Parser Char
satisfy pred = P $ \s -> case s of
[] -> ErrorResult UnexpectedEof
(c:cs) -> if pred c then Result cs c else ErrorResult (UnexpectedChar c)
char :: Char -> Parser Char
char c = satisfy (== c)
digit :: Parser Char
digit = satisfy isDigit
string :: String -> Parser String
string = mapM (\x -> char x)
choice :: [Parser a] -> Parser a
choice = foldl (<|>) failed
eof :: Parser ()
eof = P $ \s -> case s of
[] -> Result [] ()
(c:cs) -> ErrorResult (UnexpectedChar c)
count :: Int -> Parser a -> Parser [a]
count k = replicateM k
--
-- parse and return a valid phone number
-- valid phone number can be either one of
-- xxx-yyy-zzzz 858-123-4567
-- xxxyyyzzzz 8581234567
-- xxyyy 12345 Local number
-- xxx yyy zzzz 858 123 4567
localnumber :: Parser String
localnumber = count 5 digit
-- ^ parse "8581234567"
number10_1 :: Parser String
number10_1 = count 10 digit
-- ^ parse "858-123-4567"
number10_2 :: Parser String
number10_2 = do
p1 <- count 3 digit
char '-'
p2 <- count 3 digit
char '-'
p3 <- count 4 digit
return (p1++p2++p3)
-- ^ parse "858 123 4567"
number10_3 :: Parser String
number10_3 = do
p1 <- count 3 digit
char ' '
p2 <- count 3 digit
char ' '
p3 <- count 4 digit
return (p1++p2++p3)
phoneNumber = number10_1 <|> number10_2 <|> number10_3 <|> localnumber
-- ghci> parse phoneNumber "1234567890"
-- Result >< "1234567890"
--
instance Functor ParseResult where
fmap f (ErrorResult e) = ErrorResult e
fmap f (Result input a) = Result input (f a)
instance (Show a) => Show (ParseResult a) where
show (ErrorResult e) = show e
show (Result i a) = concat [ "Result >", i, "< ", show a]
instance Functor Parser where
fmap f p = P $ \input -> case parse p input of
ErrorResult e -> ErrorResult e
Result cs c -> Result cs (f c)
instance Applicative Parser where
pure x = P $ \input -> Result input x
p <*> q = P $ \input -> case parse p input of
ErrorResult e -> ErrorResult e
Result input' f -> case parse q input' of
ErrorResult e' -> ErrorResult e'
Result input'' a -> Result input'' (f a)
instance Monad Parser where
return = pure
P p >>= f = P $ \s -> case p s of
ErrorResult e -> ErrorResult e
Result s' a -> parse (f a) s'
instance Alternative Parser where
empty = P $ \_ -> ErrorResult Failed
(P f) <|> (P g) = P $ \s -> case f s of
ErrorResult e -> g s
Result s' a -> Result s' a