-
Notifications
You must be signed in to change notification settings - Fork 1
/
Parser.hs
162 lines (133 loc) · 5.51 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
module Parser (parseFile, parseString) where
import Ast (Program, Function(..), Stm(..), Expr(..), BinOp(..), Id(..), GUID)
import Control.Applicative ((<*), (<$>))
import Control.Monad (liftM)
import Text.Parsec hiding (parse)
import Text.Parsec.Expr
import qualified Text.Parsec.Token as P
import Text.Parsec.Language (emptyDef)
import Prelude hiding (fst, snd)
-- Exported functions
parseFile :: String -> IO (Either ParseError Program)
parseFile name = parse name `liftM` readFile name
parseString :: String -> Either ParseError Program
parseString = parse "<insert filename here>"
-- The lexer
opLetters = "+-*/>=&"
def = emptyDef{ P.identStart = letter
, P.identLetter = alphaNum
, P.opStart = oneOf opLetters
, P.opLetter = oneOf opLetters
, P.reservedOpNames = ["+", "-", "*", "/", ">", "==", "=", "&"]
, P.reservedNames = ["input", "output", "if", "else", "while",
"var", "return", "malloc", "null"]
, P.commentLine = "//"
, P.commentStart = "/*"
, P.commentEnd = "*/"
, P.nestedComments = False
}
P.TokenParser{ P.parens = parens
, P.braces = braces
, P.semi = semi
, P.identifier = identifier
, P.integer = integer
, P.reservedOp = reservedOp
, P.reserved = reserved
, P.semiSep1 = semiSep1
, P.commaSep = commaSep
, P.commaSep1 = commaSep1
, P.whiteSpace = whiteSpace } = P.makeTokenParser def
-- The parser
type Parser = Parsec String (GUID, [(String, GUID)], [(Int, GUID)])
fst :: (a, b, c) -> a
fst (a, b, c) = a
snd :: (a, b, c) -> b
snd (a, b, c) = b
trd :: (a, b, c) -> c
trd (a, b, c) = c
mapFst :: (a -> d) -> (a, b, c) -> (d, b, c)
mapFst f (a, b, c) = (f a, b, c)
mapSnd :: (b -> d) -> (a, b, c) -> (a, d, c)
mapSnd f (a, b, c) = (a, f b, c)
mapTrd :: (c -> d) -> (a, b, c) -> (a, b, d)
mapTrd f (a, b, c) = (a, b, f c)
guid :: Parser GUID
guid = do uid <- fst <$> getState
modifyState $ mapFst (+1)
return uid
ident :: Parser Id
ident = do id <- identifier
vars <- snd <$> getState
maybe (new id) (return . Id id) $ lookup id vars
where new id = do uid <- guid
modifyState $ mapSnd ((id, uid):)
return $ Id id uid
int :: Parser Expr
int = do i <- integer
is <- trd <$> getState
let ii = fromInteger i
maybe (new ii) (return . EConst ii) $ lookup ii is
where new i = do uid <- guid
modifyState $ mapTrd ((i, uid):)
return $ EConst i uid
parseExpr :: Parser Expr
parseExpr = buildExpressionParser table term
<?> "expression"
where term = int
<|> try (do id <- ident
es <- parens $ commaSep parseExpr
uid <- guid
return $ EAppNamed id es uid)
<|> try (do e <- parens parseExpr
es <- parens $ commaSep parseExpr
uid <- guid
return $ EAppUnnamed e es uid)
<|> EVar `liftM` ident
<|> parens parseExpr
<|> EInput `liftM` (reserved "input" >> guid)
<|> EMalloc `liftM` (reserved "malloc" >> guid)
<|> ENull `liftM` (reserved "null" >> guid)
<|> do reservedOp "&"
id <- ident
uid <- guid
return $ ERef id uid
<?> "expression term"
table = [ [pre "*" EDeRef]
, [inleft "*" (EBinOp BMulti), inleft "/" (EBinOp BDiv)]
, [inleft "+" (EBinOp BPlus), inleft "-" (EBinOp BMinus)]
, [inleft ">" (EBinOp BGt), inleft "==" (EBinOp BEq)]
]
where inleft op ast = Infix (flip2 ast `liftM` (reservedOp op >> guid)) AssocLeft
pre op ast = Prefix (flip ast `liftM` (reservedOp op >> guid))
flip2 f c a b = f a b c
parseStm :: Parser Stm
parseStm = SSeq <$> many1 stm1
where stm1 = (reserved "output" >> SOutput <$> parseExpr <* semi)
<|> (reserved "var" >> SDecl <$> commaSep1 ident <* semi)
<|> (reserved "return" >> SReturn <$> parseExpr <* semi)
<|> parseAss SAss
<|> (reservedOp "*" >> parseAss SAssRef)
<|> do reserved "if"
e <- parens parseExpr
sif <- braces parseStm
selse <- (reserved "else" >> braces parseStm) <|> return SNop
return $ SIfElse e sif selse
<|> do reserved "while"
e <- parens parseExpr
s <- braces parseStm
return $ SWhile e s
<?> "statement"
parseAss :: (Id -> Expr -> Stm) -> Parser Stm
parseAss ass = do v <- ident
reservedOp "="
e <- parseExpr
semi
return $ ass v e
parseFunction :: Parser Function
parseFunction = do name <- ident
arguments <- parens $ commaSep ident
braces $ FNamedSimple name arguments <$> parseStm
parseProgram :: Parser Program
parseProgram = whiteSpace >> many parseFunction <* eof
parse :: String -> String -> Either ParseError Program
parse = runParser parseProgram (1, [], [])