1{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
3module Core.CoreParser(parseTerm, parseFile, parseDef, pTerm, iName,
4 idrisLexer, maybeWithNS, pDocComment) where
8import Text.ParserCombinators.Parsec
9import Text.ParserCombinators.Parsec.Expr
10import Text.ParserCombinators.Parsec.Language
11import Text.ParserCombinators.Parsec.Prim
12import Text.ParserCombinators.Parsec.Char
13import Text.ParserCombinators.Parsec.Combinator
14import qualified Text.ParserCombinators.Parsec.Token as PTok
16import Control.Monad.State
17import Control.Monad.Identity
22type TokenParser a = PTok.TokenParser a
24idrisDef = haskellDef {
27 identLetter = identLetter haskellDef <|> lchar '.',
29 = [":", "..", "=", "\\", "|", "<-", "->", "=>", "**"],
31 = ["let", "in", "data", "codata", "record", "Set",
32 "do", "dsl", "import", "impossible",
33 "case", "of", "total", "partial", "mutual",
34 "infix", "infixl", "infixr",
35 "where", "with", "syntax", "proof", "postulate",
36 "using", "namespace", "class", "instance",
37 "public", "private", "abstract",
38 "Int", "Integer", "Float", "Char", "String", "Ptr"]
41iOpStart = oneOf ":!#$%&*+./<=>?@\\^|-~"
42iOpLetter = oneOf ":!#$%&*+./<=>?@\\^|-~"
45idrisLexer :: TokenParser a
46idrisLexer = idrisMakeTokenParser idrisDef
50-- Taken from Parsec source code
51lexWS = do i <- getInput
52 skipMany (simpleSpace <|> oneLineComment <|> multiLineComment <?> "")
55 simpleSpace = skipMany1 (satisfy isSpace)
56 oneLineComment = do try (do string ("--")
57 satisfy (\x -> x /= '|' && x /= '^'))
58 skipMany (satisfy (/= '\n'))
60 multiLineComment = do try (do string "{-"
61 satisfy (\x -> x /= '|' && x /= '^'))
65 = do{ try (string "-}") ; return () }
66 <|> do{ multiLineComment ; inCommentMulti }
67 <|> do{ skipMany1 (noneOf startEnd) ; inCommentMulti }
68 <|> do{ oneOf startEnd ; inCommentMulti }
73whiteSpace= PTok.whiteSpace lexer
74lexeme = PTok.lexeme lexer
75symbol = PTok.symbol lexer
76natural = PTok.natural lexer
77parens = PTok.parens lexer
79comma = PTok.comma lexer
80identifier= PTok.identifier lexer
81reserved = PTok.reserved lexer
82operator = PTok.operator lexer
83reservedOp= PTok.reservedOp lexer
84strlit = PTok.stringLiteral lexer
87type CParser a = GenParser Char a
89parseFile = runParser pTestFile () "(input)"
90parseDef = runParser pDef () "(input)"
91parseTerm = runParser pTerm () "(input)"
93pTestFile :: CParser a RProgram
94pTestFile = do p <- many1 pDef ; eof
97iName :: [String] -> CParser a Name
98iName bad = maybeWithNS identifier False bad
100-- Enhances a given parser to accept an optional namespace. All possible
101-- namespace prefixes are tried in ascending / descending order, and
102-- identifiers of a given list fail.
103maybeWithNS :: CParser a String -> Bool -> [String] -> CParser a Name
104maybeWithNS parser ascend bad = do
105 i <- option "" (lookAhead identifier)
106 when (i `elem` bad) $ fail "Reserved identifier"
107 let transf = if ascend then id else reverse
108 (x, xs) <- choice $ transf (parserNoNS : parsersNS i)
109 return $ mkName (x, xs)
111 parserNoNS = do x <- parser; return (x, "")
112 parserNS ns = do xs <- string ns; lchar '.'; x <- parser; return (x, xs)
113 parsersNS i = [try (parserNS ns) | ns <- (initsEndAt (=='.') i)]
115-- List of all initial segments in ascending order of a list. Every such
116-- initial segment ends right before an element satisfying the given
118initsEndAt :: (a -> Bool) -> [a] -> [[a]]
120initsEndAt p (x:xs) | p x = [] : x_inits_xs
121 | otherwise = x_inits_xs
122 where x_inits_xs = [x : cs | cs <- initsEndAt p xs]
124-- Create a `Name' from a pair of strings representing a base name and its
126mkName :: (String, String) -> Name
128mkName (n, ns) = NS (UN n) (reverse (parseNS ns))
129 where parseNS x = case span (/= '.') x of
131 (x, '.':y) -> x : parseNS y
133pDocComment :: Char -> CParser a String
135 = try (do string ("--" ++ [c])
137 let (doc, rest) = span (/='\n') i
141 <|> try (do string ("{-" ++ [c])
144 let (doc, rest) = spanComment "" i
148 where spanComment acc ('-':'}':xs) = (reverse acc, xs)
149 spanComment acc (x:xs) = spanComment (x : acc) xs
150 spanComment acc [] = (acc, [])
152pDef :: CParser a (Name, RDef)
153pDef = try (do x <- iName []; lchar ':'; ty <- pTerm
157 return (x, RFunction (RawFun ty tm)))
158 <|> do x <- iName []; lchar ':'; ty <- pTerm; lchar ';'
159 return (x, RConst ty)
160 <|> do (x, d) <- pData; lchar ';'
163app :: CParser a (Raw -> Raw -> Raw)
164app = do whiteSpace ; return RApp
166arrow :: CParser a (Raw -> Raw -> Raw)
167arrow = do symbol "->" ; return $ \s t -> RBind (MN 0 "X") (Pi s) t
169pTerm :: CParser a Raw
170pTerm = try (do chainl1 pNoApp app)
173pNoApp :: CParser a Raw
174pNoApp = try (chainr1 pExp arrow)
177pExp = do lchar '\\'; x <- iName []; lchar ':'; ty <- pTerm
180 return (RBind x (Lam ty) sc)
181 <|> try (do lchar '?'; x <- iName []; lchar ':'; ty <- pTerm
184 return (RBind x (Hole ty) sc))
185 <|> try (do lchar '(';
186 x <- iName []; lchar ':'; ty <- pTerm
190 return (RBind x (Pi ty) sc))
191 <|> try (do lchar '(';
195 <|> try (do symbol "??";
196 x <- iName []; lchar ':'; ty <- pTerm
200 return (RBind x (Guess ty val) sc))
201 <|> try (do reserved "let";
202 x <- iName []; lchar ':'; ty <- pTerm
207 return (RBind x (Let ty val) sc))
208 <|> try (do lchar '_';
209 x <- iName []; lchar ':'; ty <- pTerm
212 return (RBind x (PVar ty) sc))
213 <|> try (do reserved "Set"
215 <|> try (do x <- iName []
218pData :: CParser a (Name, RawDatatype)
219pData = do reserved "data"; x <- iName []; lchar ':'; ty <- pTerm; reserved "where"
220 cs <- many pConstructor
221 return (x, RDatatype x ty cs)
223pConstructor :: CParser a (Name, Raw)
224pConstructor = do lchar '|'
225 c <- iName []; lchar ':'; ty <- pTerm
228------ borrowed from Parsec
229-- (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007
231idrisMakeTokenParser languageDef
233 PTok.identifier = identifier
234 , PTok.reserved = reserved
235 , PTok.operator = operator
236 , PTok.reservedOp = reservedOp
238 , PTok.charLiteral = charLiteral
239 , PTok.stringLiteral = stringLiteral
240 , PTok.natural = natural
241 , PTok.integer = integer
243 , PTok.naturalOrFloat = naturalOrFloat
244 , PTok.decimal = decimal
245 , PTok.hexadecimal = hexadecimal
248 , PTok.symbol = symbol
249 , PTok.lexeme = lexeme
250 , PTok.whiteSpace = lexWS
252 , PTok.parens = parens
253 , PTok.braces = braces
254 , PTok.angles = angles
255 , PTok.brackets = brackets
256 , PTok.squares = brackets
261 , PTok.semiSep = semiSep
262 , PTok.semiSep1 = semiSep1
263 , PTok.commaSep = commaSep
264 , PTok.commaSep1 = commaSep1
268 -----------------------------------------------------------
270 -----------------------------------------------------------
271 parens p = between (symbol "(") (symbol ")") p
272 braces p = between (symbol "{") (symbol "}") p
273 angles p = between (symbol "<") (symbol ">") p
274 brackets p = between (symbol "[") (symbol "]") p
281 commaSep p = sepBy p comma
282 semiSep p = sepBy p semi
284 commaSep1 p = sepBy1 p comma
285 semiSep1 p = sepBy1 p semi
288 -----------------------------------------------------------
290 -----------------------------------------------------------
291 charLiteral = lexeme (between (char '\'')
292 (char '\'' <?> "end of character")
296 characterChar = charLetter <|> charEscape
297 <?> "literal character"
299 charEscape = do{ char '\\'; escapeCode }
300 charLetter = satisfy (\c -> (c /= '\'') && (c /= '\\') && (c > '\026'))
304 stringLiteral = lexeme (
305 do{ str <- between (char '"')
306 (char '"' <?> "end of string")
308 ; return (foldr (maybe id (:)) "" str)
310 <?> "literal string")
312 stringChar = do{ c <- stringLetter; return (Just c) }
314 <?> "string character"
316 stringLetter = satisfy (\c -> (c /= '"') && (c /= '\\') && (c > '\026'))
318 stringEscape = do{ char '\\'
319 ; do{ escapeGap ; return Nothing }
320 <|> do{ escapeEmpty; return Nothing }
321 <|> do{ esc <- escapeCode; return (Just esc) }
324 escapeEmpty = char '&'
325 escapeGap = do{ many1 space
326 ; char '\\' <?> "end of string gap"
332 escapeCode = charEsc <|> charNum <|> charAscii <|> charControl
335 charControl = do{ char '^'
337 ; return (toEnum (fromEnum code - fromEnum 'A'))
340 charNum = do{ code <- decimal
341 <|> do{ char 'o'; number 8 octDigit }
342 <|> do{ char 'x'; number 16 hexDigit }
343 ; return (toEnum (fromInteger code))
346 charEsc = choice (map parseEsc escMap)
348 parseEsc (c,code) = do{ char c; return code }
350 charAscii = choice (map parseAscii asciiMap)
352 parseAscii (asc,code) = try (do{ string asc; return code })
355 -- escape code tables
356 escMap = zip ("abfnrtv\\\"\'") ("\a\b\f\n\r\t\v\\\"\'")
357 asciiMap = zip (ascii3codes ++ ascii2codes) (ascii3 ++ ascii2)
359 ascii2codes = ["BS","HT","LF","VT","FF","CR","SO","SI","EM",
360 "FS","GS","RS","US","SP"]
361 ascii3codes = ["NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL",
362 "DLE","DC1","DC2","DC3","DC4","NAK","SYN","ETB",
363 "CAN","SUB","ESC","DEL"]
365 ascii2 = ['\BS','\HT','\LF','\VT','\FF','\CR','\SO','\SI',
366 '\EM','\FS','\GS','\RS','\US','\SP']
367 ascii3 = ['\NUL','\SOH','\STX','\ETX','\EOT','\ENQ','\ACK',
368 '\BEL','\DLE','\DC1','\DC2','\DC3','\DC4','\NAK',
369 '\SYN','\ETB','\CAN','\SUB','\ESC','\DEL']
372 -----------------------------------------------------------
374 -----------------------------------------------------------
375 naturalOrFloat = lexeme (natFloat) <?> "number"
377 float = lexeme floating <?> "float"
378 integer = lexeme int <?> "integer"
379 natural = lexeme nat <?> "natural"
383 floating = do{ n <- decimal
388 natFloat = do{ char '0'
393 zeroNumFloat = do{ n <- hexadecimal <|> octal
400 decimalFloat = do{ n <- decimal
405 fractFloat n = do{ f <- fractExponent n
409 fractExponent n = do{ fract <- fraction
410 ; expo <- option 1.0 exponent'
411 ; return ((fromInteger n + fract)*expo)
414 do{ expo <- exponent'
415 ; return ((fromInteger n)*expo)
418 fraction = do{ char '.'
419 ; digits <- many1 digit <?> "fraction"
420 ; return (foldr op 0.0 digits)
424 op d f = (f + fromIntegral (digitToInt d))/10.0
426 exponent' = do{ oneOf "eE"
428 ; e <- decimal <?> "exponent"
429 ; return (power (f e))
433 power e | e < 0 = 1.0/power(-e)
434 | otherwise = fromInteger (10^e)
437 -- integers and naturals
438 int = do{ f <- lexeme sign
443 sign = (char '-' >> return negate)
444 <|> (char '+' >> return id)
447 nat = zeroNumber <|> decimal
449 zeroNumber = do{ char '0'
450 ; hexadecimal <|> octal <|> decimal <|> return 0
454 decimal = number 10 digit
455 hexadecimal = do{ oneOf "xX"; number 16 hexDigit }
456 octal = do{ oneOf "oO"; number 8 octDigit }
458 number base baseDigit
459 = do{ digits <- many1 baseDigit
460 ; let n = foldl (\x d -> base*x + toInteger (digitToInt d)) 0 digits
464 -----------------------------------------------------------
465 -- Operators & reserved ops
466 -----------------------------------------------------------
470 ; notFollowedBy (opLetter languageDef) <?> ("end of " ++ show name)
476 ; if (isReservedOp name)
477 then unexpected ("reserved operator " ++ show name)
482 do{ c <- (opStart languageDef)
483 ; cs <- many (opLetter languageDef)
489 isReserved (sort (reservedOpNames languageDef)) name
492 -----------------------------------------------------------
493 -- Identifiers & Reserved words
494 -----------------------------------------------------------
498 ; notFollowedBy (identLetter languageDef) <?> ("end of " ++ show name)
502 | caseSensitive languageDef = string name
503 | otherwise = do{ walk name; return name }
506 walk (c:cs) = do{ caseChar c <?> msg; walk cs }
508 caseChar c | isAlpha c = char (toLower c) <|> char (toUpper c)
517 ; if (isReservedName name)
518 then unexpected ("reserved word " ++ show name)
524 = do{ c <- identStart languageDef
525 ; cs <- many (identLetter languageDef)
531 = isReserved theReservedNames caseName
533 caseName | caseSensitive languageDef = name
534 | otherwise = map toLower name
537 isReserved names name
541 scan (r:rs) = case (compare r name) of
547 | caseSensitive languageDef = sortedNames
548 | otherwise = map (map toLower) sortedNames
550 sortedNames = sort (reservedNames languageDef)
554 -----------------------------------------------------------
555 -- White space & symbols
556 -----------------------------------------------------------
558 = lexeme (string name)
561 = do{ x <- p; whiteSpace; return x }
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list