1{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
3module Core.CoreParser(parseTerm, parseFile, parseDef, pTerm, iName,
4 idrisLexer, maybeWithNS, pDocComment, opChars) 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", "Type",
32 "do", "dsl", "import", "impossible",
33 "case", "of", "total", "partial", "mutual",
34 "infix", "infixl", "infixr", "rewrite",
35 "where", "with", "syntax", "proof", "postulate",
36 "using", "namespace", "class", "instance",
37 "public", "private", "abstract", "implicit",
38 "Int", "Integer", "Float", "Char", "String", "Ptr",
39 "Bits8", "Bits16", "Bits32", "Bits64",
40 "Bits8x16", "Bits16x8", "Bits32x4", "Bits64x2"]
43-- | The characters allowed in operator names
44opChars = ":!#$%&*+./<=>?@\\^|-~"
46iOpStart = oneOf opChars
47iOpLetter = oneOf opChars
50idrisLexer :: TokenParser a
51idrisLexer = idrisMakeTokenParser idrisDef
55-- Taken from Parsec source code
56lexWS = do i <- getInput
57 skipMany (simpleSpace <|> oneLineComment <|> multiLineComment <?> "")
60 simpleSpace = skipMany1 (satisfy isSpace)
62 try (void $ string "--\n") <|>
63 do try (do string "--"
64 satisfy (\x -> x /= '|' && x /= '^'))
65 skipMany (satisfy (/= '\n'))
67 try (void $ string "{--}") <|>
68 do try (do string "{-"
69 satisfy (\x -> x /= '|' && x /= '^'))
73 = do{ try (string "-}") ; return () }
74 <|> do{ multiLineComment ; inCommentMulti }
75 <|> do{ skipMany1 (noneOf startEnd) ; inCommentMulti }
76 <|> do{ oneOf startEnd ; inCommentMulti }
81whiteSpace= PTok.whiteSpace lexer
82lexeme = PTok.lexeme lexer
83symbol = PTok.symbol lexer
84natural = PTok.natural lexer
85parens = PTok.parens lexer
87comma = PTok.comma lexer
88identifier= PTok.identifier lexer
89reserved = PTok.reserved lexer
90operator = PTok.operator lexer
91reservedOp= PTok.reservedOp lexer
92strlit = PTok.stringLiteral lexer
95type CParser a = GenParser Char a
97parseFile = runParser pTestFile () "(input)"
98parseDef = runParser pDef () "(input)"
99parseTerm = runParser pTerm () "(input)"
101pTestFile :: CParser a RProgram
102pTestFile = do p <- many1 pDef ; eof
105iName :: [String] -> CParser a Name
106iName bad = maybeWithNS identifier False bad
108-- Enhances a given parser to accept an optional namespace. All possible
109-- namespace prefixes are tried in ascending / descending order, and
110-- identifiers of a given list fail.
111maybeWithNS :: CParser a String -> Bool -> [String] -> CParser a Name
112maybeWithNS parser ascend bad = do
113 i <- option "" (lookAhead identifier)
114 when (i `elem` bad) $ fail "Reserved identifier"
115 let transf = if ascend then id else reverse
116 (x, xs) <- choice $ transf (parserNoNS : parsersNS i)
117 return $ mkName (x, xs)
119 parserNoNS = do x <- parser; return (x, "")
120 parserNS ns = do xs <- string ns; lchar '.'; x <- parser; return (x, xs)
121 parsersNS i = [try (parserNS ns) | ns <- (initsEndAt (=='.') i)]
123-- List of all initial segments in ascending order of a list. Every such
124-- initial segment ends right before an element satisfying the given
126initsEndAt :: (a -> Bool) -> [a] -> [[a]]
128initsEndAt p (x:xs) | p x = [] : x_inits_xs
129 | otherwise = x_inits_xs
130 where x_inits_xs = [x : cs | cs <- initsEndAt p xs]
132-- Create a `Name' from a pair of strings representing a base name and its
134mkName :: (String, String) -> Name
136mkName (n, ns) = NS (UN n) (reverse (parseNS ns))
137 where parseNS x = case span (/= '.') x of
139 (x, '.':y) -> x : parseNS y
141pDocComment :: Char -> CParser a String
143 = try (do string ("--")
147 let (doc, rest) = span (/='\n') i
151 <|> try (do string ("{-")
156 let (doc, rest) = spanComment "" i
160 where spanComment acc ('-':'}':xs) = (reverse acc, xs)
161 spanComment acc (x:xs) = spanComment (x : acc) xs
162 spanComment acc [] = (acc, [])
163 simpleSpace = skipMany1 (satisfy isSpace)
165pDef :: CParser a (Name, RDef)
166pDef = try (do x <- iName []; lchar ':'; ty <- pTerm
170 return (x, RFunction (RawFun ty tm)))
171 <|> do x <- iName []; lchar ':'; ty <- pTerm; lchar ';'
172 return (x, RConst ty)
173 <|> do (x, d) <- pData; lchar ';'
176app :: CParser a (Raw -> Raw -> Raw)
177app = do whiteSpace ; return RApp
179arrow :: CParser a (Raw -> Raw -> Raw)
180arrow = do symbol "->" ; return $ \s t -> RBind (MN 0 "X") (Pi s) t
182pTerm :: CParser a Raw
183pTerm = try (do chainl1 pNoApp app)
186pNoApp :: CParser a Raw
187pNoApp = try (chainr1 pExp arrow)
190pExp = do lchar '\\'; x <- iName []; lchar ':'; ty <- pTerm
193 return (RBind x (Lam ty) sc)
194 <|> try (do lchar '?'; x <- iName []; lchar ':'; ty <- pTerm
197 return (RBind x (Hole ty) sc))
198 <|> try (do lchar '(';
199 x <- iName []; lchar ':'; ty <- pTerm
203 return (RBind x (Pi ty) sc))
204 <|> try (do lchar '(';
208 <|> try (do symbol "??";
209 x <- iName []; lchar ':'; ty <- pTerm
213 return (RBind x (Guess ty val) sc))
214 <|> try (do reserved "let";
215 x <- iName []; lchar ':'; ty <- pTerm
220 return (RBind x (Let ty val) sc))
221 <|> try (do lchar '_';
222 x <- iName []; lchar ':'; ty <- pTerm
225 return (RBind x (PVar ty) sc))
226 <|> try (do reserved "Type"
228 <|> try (do x <- iName []
231pData :: CParser a (Name, RawDatatype)
232pData = do reserved "data"; x <- iName []; lchar ':'; ty <- pTerm; reserved "where"
233 cs <- many pConstructor
234 return (x, RDatatype x ty cs)
236pConstructor :: CParser a (Name, Raw)
237pConstructor = do lchar '|'
238 c <- iName []; lchar ':'; ty <- pTerm
241------ borrowed from Parsec
242-- (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007
244idrisMakeTokenParser languageDef
246 PTok.identifier = identifier
247 , PTok.reserved = reserved
248 , PTok.operator = operator
249 , PTok.reservedOp = reservedOp
251 , PTok.charLiteral = charLiteral
252 , PTok.stringLiteral = stringLiteral
253 , PTok.natural = natural
254 , PTok.integer = integer
256 , PTok.naturalOrFloat = naturalOrFloat
257 , PTok.decimal = decimal
258 , PTok.hexadecimal = hexadecimal
261 , PTok.symbol = symbol
262 , PTok.lexeme = lexeme
263 , PTok.whiteSpace = lexWS
265 , PTok.parens = parens
266 , PTok.braces = braces
267 , PTok.angles = angles
268 , PTok.brackets = brackets
269 , PTok.squares = brackets
274 , PTok.semiSep = semiSep
275 , PTok.semiSep1 = semiSep1
276 , PTok.commaSep = commaSep
277 , PTok.commaSep1 = commaSep1
281 -----------------------------------------------------------
283 -----------------------------------------------------------
284 parens p = between (symbol "(") (symbol ")") p
285 braces p = between (symbol "{") (symbol "}") p
286 angles p = between (symbol "<") (symbol ">") p
287 brackets p = between (symbol "[") (symbol "]") p
294 commaSep p = sepBy p comma
295 semiSep p = sepBy p semi
297 commaSep1 p = sepBy1 p comma
298 semiSep1 p = sepBy1 p semi
301 -----------------------------------------------------------
303 -----------------------------------------------------------
304 charLiteral = lexeme (between (char '\'')
305 (char '\'' <?> "end of character")
309 characterChar = charLetter <|> charEscape
310 <?> "literal character"
312 charEscape = do{ char '\\'; escapeCode }
313 charLetter = satisfy (\c -> (c /= '\'') && (c /= '\\') && (c > '\026'))
317 stringLiteral = lexeme (
318 do{ str <- between (char '"')
319 (char '"' <?> "end of string")
321 ; return (foldr (maybe id (:)) "" str)
323 <?> "literal string")
325 stringChar = do{ c <- stringLetter; return (Just c) }
327 <?> "string character"
329 stringLetter = satisfy (\c -> (c /= '"') && (c /= '\\') && (c > '\026'))
331 stringEscape = do{ char '\\'
332 ; do{ escapeGap ; return Nothing }
333 <|> do{ escapeEmpty; return Nothing }
334 <|> do{ esc <- escapeCode; return (Just esc) }
337 escapeEmpty = char '&'
338 escapeGap = do{ many1 space
339 ; char '\\' <?> "end of string gap"
345 escapeCode = charEsc <|> charNum <|> charAscii <|> charControl
348 charControl = do{ char '^'
350 ; return (toEnum (fromEnum code - fromEnum 'A'))
353 charNum = do{ code <- decimal
354 <|> do{ char 'o'; number 8 octDigit }
355 <|> do{ char 'x'; number 16 hexDigit }
356 ; return (toEnum (fromInteger code))
359 charEsc = choice (map parseEsc escMap)
361 parseEsc (c,code) = do{ char c; return code }
363 charAscii = choice (map parseAscii asciiMap)
365 parseAscii (asc,code) = try (do{ string asc; return code })
368 -- escape code tables
369 escMap = zip ("abfnrtv\\\"\'") ("\a\b\f\n\r\t\v\\\"\'")
370 asciiMap = zip (ascii3codes ++ ascii2codes) (ascii3 ++ ascii2)
372 ascii2codes = ["BS","HT","LF","VT","FF","CR","SO","SI","EM",
373 "FS","GS","RS","US","SP"]
374 ascii3codes = ["NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL",
375 "DLE","DC1","DC2","DC3","DC4","NAK","SYN","ETB",
376 "CAN","SUB","ESC","DEL"]
378 ascii2 = ['\BS','\HT','\LF','\VT','\FF','\CR','\SO','\SI',
379 '\EM','\FS','\GS','\RS','\US','\SP']
380 ascii3 = ['\NUL','\SOH','\STX','\ETX','\EOT','\ENQ','\ACK',
381 '\BEL','\DLE','\DC1','\DC2','\DC3','\DC4','\NAK',
382 '\SYN','\ETB','\CAN','\SUB','\ESC','\DEL']
385 -----------------------------------------------------------
387 -----------------------------------------------------------
388 naturalOrFloat = lexeme (natFloat) <?> "number"
390 float = lexeme floating <?> "float"
391 integer = lexeme int <?> "integer"
392 natural = lexeme nat <?> "natural"
396 floating = do{ n <- decimal
401 natFloat = do{ char '0'
406 zeroNumFloat = do{ n <- hexadecimal <|> octal
413 decimalFloat = do{ n <- decimal
418 fractFloat n = do{ f <- fractExponent n
422 fractExponent n = do{ fract <- fraction
423 ; expo <- option 1.0 exponent'
424 ; return ((fromInteger n + fract)*expo)
427 do{ expo <- exponent'
428 ; return ((fromInteger n)*expo)
431 fraction = do{ char '.'
432 ; digits <- many1 digit <?> "fraction"
433 ; return (foldr op 0.0 digits)
437 op d f = (f + fromIntegral (digitToInt d))/10.0
439 exponent' = do{ oneOf "eE"
441 ; e <- decimal <?> "exponent"
442 ; return (power (f e))
446 power e | e < 0 = 1.0/power(-e)
447 | otherwise = fromInteger (10^e)
450 -- integers and naturals
451 int = do{ f <- lexeme sign
456 sign = (char '-' >> return negate)
457 <|> (char '+' >> return id)
460 nat = zeroNumber <|> decimal
462 zeroNumber = do{ char '0'
463 ; hexadecimal <|> octal <|> decimal <|> return 0
467 decimal = number 10 digit
468 hexadecimal = do{ oneOf "xX"; number 16 hexDigit }
469 octal = do{ oneOf "oO"; number 8 octDigit }
471 number base baseDigit
472 = do{ digits <- many1 baseDigit
473 ; let n = foldl' (\x d -> base*x + toInteger (digitToInt d)) 0 digits
477 -----------------------------------------------------------
478 -- Operators & reserved ops
479 -----------------------------------------------------------
483 ; notFollowedBy (opLetter languageDef) <?> ("end of " ++ show name)
489 ; if (isReservedOp name)
490 then unexpected ("reserved operator " ++ show name)
495 do{ c <- (opStart languageDef)
496 ; cs <- many (opLetter languageDef)
502 isReserved (sort (reservedOpNames languageDef)) name
505 -----------------------------------------------------------
506 -- Identifiers & Reserved words
507 -----------------------------------------------------------
511 ; notFollowedBy (identLetter languageDef) <?> ("end of " ++ show name)
515 | caseSensitive languageDef = string name
516 | otherwise = do{ walk name; return name }
519 walk (c:cs) = do{ caseChar c <?> msg; walk cs }
521 caseChar c | isAlpha c = char (toLower c) <|> char (toUpper c)
530 ; if (isReservedName name)
531 then unexpected ("reserved word " ++ show name)
537 = do{ c <- identStart languageDef
538 ; cs <- many (identLetter languageDef)
544 = isReserved theReservedNames caseName
546 caseName | caseSensitive languageDef = name
547 | otherwise = map toLower name
550 isReserved names name
554 scan (r:rs) = case (compare r name) of
560 | caseSensitive languageDef = sortedNames
561 | otherwise = map (map toLower) sortedNames
563 sortedNames = sort (reservedNames languageDef)
567 -----------------------------------------------------------
568 -- White space & symbols
569 -----------------------------------------------------------
571 = lexeme (string name)
574 = 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