-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.hs
More file actions
132 lines (105 loc) · 3.85 KB
/
main.hs
File metadata and controls
132 lines (105 loc) · 3.85 KB
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
module Main where
import Data.Char(isDigit,isAlpha)
import Control.Monad
import Test.HUnit
--------------------------------------------------------
-- Parser type
--------------------------------------------------------
data Parser a = Parser (String -> Maybe (String,a))
instance Monad Parser where
return x = Parser (\s -> Just (s,x))
(Parser p) >>= f =
Parser $ \s ->
case p s of
Nothing -> Nothing
Just (s',x) -> let (Parser p') = f x in p' s'
instance Functor Parser where
fmap f (Parser p) = Parser $ \s -> case p s of
Nothing -> Nothing
Just(s,x) -> Just (s, f x)
instance MonadPlus Parser where
mzero = Parser (\s -> Nothing)
(Parser pa) `mplus` (Parser pb) = Parser $ \s ->
case pa s of
Nothing -> pb s
x -> x
--------------------------------------------------------
-- Parser combinators
--------------------------------------------------------
match :: (Char -> Bool) -> Parser Char
match f = Parser $ \s ->
case s of
(x:xs) -> if f x then Just (xs, x) else Nothing
_ -> Nothing
char :: Char -> Parser Char
char c = match (c==)
digit :: Parser Char
digit = match isDigit
int :: Parser Int
int = fmap read $ many1 digit
alphaNum :: Parser Char
alphaNum = match (\c -> isAlpha c || isDigit c)
space :: Parser Char
space = match (\c -> c == ' ' || c == '\t')
spaces :: Parser [Char]
spaces = many space
option :: a -> Parser a -> Parser a
option dx (Parser p) = Parser $ \s ->
case p s of
Just (s',x) -> Just (s',x)
_ -> Just(s, dx)
many :: Parser a -> Parser [a]
many (Parser p) = fmap reverse $ Parser $ \s -> scan s []
where
scan s xs = case p s of
Nothing -> Just (s, xs)
Just(s',x) -> scan s' (x:xs)
many1 :: Parser a -> Parser [a]
many1 p = do
x <- p
xs <- option [] (many1 p)
return (x:xs)
(<|>) :: Parser a -> Parser a -> Parser a
p1 <|> p2 = p1 `mplus` p2
sepBy :: Parser a -> Parser b -> Parser [a]
sepBy p s = do
x <- p
xs <- many (s >> p)
return (x:xs)
brackes :: Parser bl -> Parser a -> Parser br -> Parser a
brackes pbl p pbr = do
pbl >> spaces
x <- p
spaces >> pbr
return x
cbrackes :: Parser a -> Parser a
cbrackes p = brackes (char '{') p (char '}')
--------------------------------------------------------
-- Parser helpers
--------------------------------------------------------
parse :: Parser a -> String -> Maybe a
parse (Parser p) s =
case p s of
Just (_,x) -> Just x
_ -> Nothing
--------------------------------------------------------
-- Unit tests
--------------------------------------------------------
tests = test [
"test 1" ~: parse (char 'a' >> char 'b' >> char 'c') "abc" @=? (Just 'c')
, "test 2" ~: parse (many digit >> char 'a') "1234a" @=? (Just 'a')
, "test 3" ~: parse int "154" @=? (Just 154)
, "test 4" ~: parse (many (char 'a' <|> char 'b')) "abababbac" @=? (Just "abababba")
, "test 5" ~: parse (sepBy (many alphaNum) (char ',')) "a23x,2,ba" @=? (Just ["a23x","2","ba"])
, "test 6" ~: parse (cbrackes (many1 alphaNum)) "{ dssaa }" @=? (Just "dssaa")
, "test 7" ~: parse p7 "f1a = { 10, 20, 30 }" @=? (Just ("f1a", [10,20,30]))
]
where
p7 = do
label <- many1 alphaNum
spaces
char '='
spaces
numbers <- cbrackes (sepBy (spaces>>int) (char ','))
return (label, numbers)
main = runTestTT tests