Commit 10991eb1 authored by Razzi Abuissa's avatar Razzi Abuissa

Implement pragma handling

parent 1c3d12c5
......@@ -67,7 +67,7 @@ data ParseOutput a
-- | Store locations along with a value.
data Located a = Located {
line :: LineNumber, -- Where this element is located.
unloc :: a -- Located element.
unloc :: a -- Located element.
} deriving (Eq, Show, Functor)
......@@ -101,17 +101,17 @@ runParser flags (Parser parserType parser) str =
toParseOut $ unP parser parseState
where
toParseOut :: ParseResult a -> ParseOutput a
toParseOut (PFailed span@(RealSrcSpan realSpan) err) =
toParseOut (PFailed span@(RealSrcSpan realSpan) err) =
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err
line = srcLocLine $ realSrcSpanStart realSpan
col = srcLocCol $ realSrcSpanStart realSpan
in Failure errMsg $ Loc line col
toParseOut (PFailed span err) =
toParseOut (PFailed span err) =
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err
in Failure errMsg $ Loc 0 0
toParseOut (POk parseState result) =
toParseOut (POk parseState result) =
let parseEnd = realSrcSpanStart $ last_loc parseState
endLine = srcLocLine parseEnd
endCol = srcLocCol parseEnd
......@@ -126,7 +126,7 @@ runParser flags (Parser parserType parser) str =
-- | Split a string at a given line and column. The column is included in
-- the second part of the split.
splitAtLoc :: LineNumber -> ColumnNumber -> String -> (String, String)
splitAtLoc line col string =
splitAtLoc line col string =
if line > length (lines string)
then (string, "")
else (before, after)
......@@ -145,7 +145,7 @@ joinLines = intercalate "\n"
-- | Split an input string into chunks based on indentation.
-- A chunk is a line and all lines immediately following that are indented
-- beyond the indentation of the first line. This parses Haskell layout
-- rules properly, and allows using multiline expressions via indentation.
-- rules properly, and allows using multiline expressions via indentation.
layoutChunks :: String -> [Located String]
layoutChunks = go 1
where
......@@ -164,16 +164,16 @@ layoutChunks = go 1
layoutLines _ [] = []
-- Use the indent of the first line to find the end of the first block.
layoutLines lineIdx all@(firstLine:rest) =
layoutLines lineIdx all@(firstLine:rest) =
let firstIndent = indentLevel firstLine
blockEnded line = indentLevel line <= firstIndent in
case findIndex blockEnded rest of
-- If the first block doesn't end, return the whole string, since
-- that just means the block takes up the entire string.
-- that just means the block takes up the entire string.
Nothing -> [Located lineIdx $ intercalate "\n" all]
-- We found the end of the block. Split this bit out and recurse.
Just idx ->
Just idx ->
let (before, after) = splitAt idx rest in
Located lineIdx (joinLines $ firstLine:before) : go (lineIdx + idx + 1) (joinLines after)
......@@ -183,7 +183,7 @@ layoutChunks = go 1
-- Count a tab as two spaces.
indentLevel ('\t':str) = 2 + indentLevel str
-- Count empty lines as a large indent level, so they're always with the previous expression.
indentLevel "" = 100000
......@@ -192,7 +192,7 @@ layoutChunks = go 1
-- | Drop comments from Haskell source.
-- Simply gets rid of them, does not replace them in any way.
removeComments :: String -> String
removeComments = removeOneLineComments . removeMultilineComments 0
removeComments = removeOneLineComments . removeMultilineComments 0 0
where
removeOneLineComments str =
case str of
......@@ -200,7 +200,7 @@ removeComments = removeOneLineComments . removeMultilineComments 0
':':'!':remaining ->":!" ++ takeLine remaining ++ dropLine remaining
-- Handle strings.
'"':remaining ->
'"':remaining ->
let quoted = takeString remaining
len = length quoted in
'"':quoted ++ removeOneLineComments (drop len remaining)
......@@ -211,31 +211,40 @@ removeComments = removeOneLineComments . removeMultilineComments 0
where
dropLine = removeOneLineComments . dropWhile (/= '\n')
removeMultilineComments nesting str =
removeMultilineComments nesting pragmaNesting str =
case str of
-- Don't remove comments after cmd directives
':':'!':remaining ->":!" ++ takeLine remaining ++
removeMultilineComments nesting (dropWhile (/= '\n') remaining)
removeMultilineComments nesting pragmaNesting (dropWhile (/= '\n') remaining)
-- Handle strings.
'"':remaining ->
'"':remaining ->
if nesting == 0
then
then
let quoted = takeString remaining
len = length quoted in
'"':quoted ++ removeMultilineComments nesting (drop len remaining)
'"':quoted ++ removeMultilineComments nesting pragmaNesting (drop len remaining)
else
removeMultilineComments nesting remaining
'{':'-':remaining -> removeMultilineComments (nesting + 1) remaining
'-':'}':remaining ->
removeMultilineComments nesting pragmaNesting remaining
'{':'-':'#':remaining ->
if nesting == 0
then "{-#" ++ removeMultilineComments nesting (pragmaNesting + 1) remaining
else removeMultilineComments nesting pragmaNesting remaining
'#':'-':'}':remaining ->
if nesting == 0
then if pragmaNesting > 0
then '#':'-':'}':removeMultilineComments nesting (pragmaNesting - 1) remaining
else '#':'-':'}':removeMultilineComments nesting pragmaNesting remaining
else removeMultilineComments nesting pragmaNesting remaining
'{':'-':remaining -> removeMultilineComments (nesting + 1) pragmaNesting remaining
'-':'}':remaining ->
if nesting > 0
then removeMultilineComments (nesting - 1) remaining
else '-':'}':removeMultilineComments nesting remaining
x:xs ->
then removeMultilineComments (nesting - 1) pragmaNesting remaining
else '-':'}':removeMultilineComments nesting pragmaNesting remaining
x:xs ->
if nesting > 0
then removeMultilineComments nesting xs
else x:removeMultilineComments nesting xs
then removeMultilineComments nesting pragmaNesting xs
else x:removeMultilineComments nesting pragmaNesting xs
[] -> []
takeLine = takeWhile (/= '\n')
......
......@@ -259,7 +259,7 @@ evaluate kernelState code output = do
-- Merge them with normal display outputs.
dispsIO <- extractValue "IHaskell.Display.displayFromChan"
dispsMay <- liftIO dispsIO
let result =
let result =
case dispsMay of
Nothing -> evalResult evalOut
Just disps -> evalResult evalOut <> disps
......@@ -416,7 +416,7 @@ evalCommand output (Directive SetDynFlag flags) state =
-- For -XNoImplicitPrelude, remove the Prelude import.
-- For -XImplicitPrelude, add it back in.
case flag of
"-XNoImplicitPrelude" ->
"-XNoImplicitPrelude" ->
evalImport "import qualified Prelude as Prelude"
"-XImplicitPrelude" -> do
importDecl <- parseImportDecl "import Prelude"
......@@ -882,6 +882,9 @@ evalCommand _ (ParseError loc err) state = do
evalComms = []
}
evalCommand output (Pragma pragmas) state = do
write $ "Got pragmas " ++ show pragmas
evalCommand output (Directive SetExtension $ unwords pragmas) state
hoogleResults :: KernelState -> [Hoogle.HoogleResult] -> EvalOut
hoogleResults state results = EvalOut {
......@@ -987,7 +990,7 @@ keepingItVariable act = do
var name = name ++ rand
goStmt s = runStmt s RunToCompletion
itVariable = var "it_var_temp_"
goStmt $ printf "let %s = it" itVariable
val <- act
goStmt $ printf "let it = %s" itVariable
......
......@@ -48,6 +48,7 @@ data CodeBlock
| Directive DirectiveType String -- ^ An IHaskell directive.
| Module String -- ^ A full Haskell module, to be compiled and loaded.
| ParseError StringLoc ErrMsg -- ^ An error indicating that parsing the code block failed.
| Pragma [String] -- ^ A list of GHC pragmas (from a {-# LANGUAGE ... #-} block)
deriving (Show, Eq)
-- | Directive types. Each directive is associated with a string in the
......@@ -86,10 +87,11 @@ parseString codeString = do
return result
where
parseChunk :: GhcMonad m => String -> LineNumber -> m (Located CodeBlock)
parseChunk chunk line = Located line <$>
if isDirective chunk
then return $ parseDirective chunk line
else parseCodeChunk chunk line
parseChunk chunk line
| isDirective chunk = return $ Located line $ parseDirective chunk line
| isPragma chunk = return $ Located line $ parsePragma chunk line
| otherwise = Located line <$> parseCodeChunk chunk line
processChunks :: GhcMonad m => [Located CodeBlock] -> [Located String] -> m [Located CodeBlock]
processChunks accum remaining =
......@@ -100,25 +102,39 @@ parseString codeString = do
-- If we have more remaining, parse the current chunk and recurse.
Located line chunk:remaining -> do
block <- parseChunk chunk line
activateParsingExtensions $ unloc block
activateExtensions $ unloc block
processChunks (block : accum) remaining
-- Test wither a given chunk is a directive.
-- Test whether a given chunk is a directive.
isDirective :: String -> Bool
isDirective = startswith ":" . strip
-- Test if a chunk is a pragma.
isPragma :: String -> Bool
isPragma = startswith "{-#" . strip
-- Number of lines in this string.
nlines :: String -> Int
nlines = length . lines
activateParsingExtensions :: GhcMonad m => CodeBlock -> m ()
activateParsingExtensions (Directive SetExtension ext) = void $ setExtension ext
activateParsingExtensions (Directive SetDynFlag flags) =
activateExtensions :: GhcMonad m => CodeBlock -> m ()
activateExtensions (Directive SetExtension ext) = void $ setExtension ext
activateExtensions (Directive SetDynFlag flags) =
case stripPrefix "-X" flags of
Just ext -> void $ setExtension ext
Nothing -> return ()
activateParsingExtensions _ = return ()
activateExtensions (Pragma extensions) = void $ setAll extensions
where
setAll :: GhcMonad m => [String] -> m (Maybe String)
setAll (ext:extensions) = do
err <- setExtension ext
case err of
Nothing -> setAll extensions
Just err -> return $ Just err
setAll [] = return Nothing
activateExtensions _ = return ()
-- | Parse a single chunk of code, as indicated by the layout of the code.
parseCodeChunk :: GhcMonad m => String -> LineNumber -> m CodeBlock
parseCodeChunk code startLine = do
......@@ -191,11 +207,11 @@ parseCodeChunk code startLine = do
-- signature, which is also joined with the subsequent declarations.
joinFunctions :: [Located CodeBlock] -> [Located CodeBlock]
joinFunctions [] = []
joinFunctions blocks =
joinFunctions blocks =
if signatureOrDecl $ unloc $ head blocks
then Located lnum (conjoin $ map unloc decls) : joinFunctions rest
else head blocks : joinFunctions (tail blocks)
where
where
decls = takeWhile (signatureOrDecl . unloc) blocks
rest = drop (length decls) blocks
lnum = line $ head decls
......@@ -211,6 +227,20 @@ joinFunctions blocks =
conjoin :: [CodeBlock] -> CodeBlock
conjoin = Declaration . intercalate "\n" . map str
-- | Parse a pragma of the form {-# LANGUAGE ... #-}
parsePragma :: String -- ^ Pragma string.
-> Int -- ^ Line number at which the directive appears.
-> CodeBlock -- ^ Pragma code block or a parse error.
parsePragma ('{':'-':'#':pragma) line = Pragma $ extractPragma pragma
where
extractPragma :: String -> [String]
-- | After removing commas, extract words until a # is reached
extractPragma pragmas = case (words $ takeWhile (/= '#') $ filter (/= ',' ) pragmas) of
[] -> []
x:xs -> xs -- remove the first word (such as LANGUAGE)
-- | Parse a directive of the form :directiveName.
parseDirective :: String -- ^ Directive string.
-> Int -- ^ Line number at which the directive appears.
......
......@@ -52,7 +52,7 @@ data ExtFlag
-- If no such extension exist, yield @Nothing@.
extensionFlag :: String -- Extension name, such as @"DataKinds"@
-> Maybe ExtFlag
extensionFlag ext =
extensionFlag ext =
case find (flagMatches ext) xFlags of
Just (_, flag, _) -> Just $ SetFlag flag
-- If it doesn't match an extension name, try matching against
......@@ -68,7 +68,7 @@ extensionFlag ext =
-- Check if a FlagSpec matches "No<ExtensionName>".
-- In that case, we disable the extension.
flagMatchesNo ext (name, _, _) = ext == "No" ++ name
flagMatchesNo ext (name, _, _) = ext == "No" ++ name
-- | Set an extension and update flags.
-- Return @Nothing@ on success. On failure, return an error message.
......@@ -78,7 +78,7 @@ setExtension ext = do
case extensionFlag ext of
Nothing -> return $ Just $ "Could not parse extension name: " ++ ext
Just flag -> do
setSessionDynFlags $
setSessionDynFlags $
case flag of
SetFlag ghcFlag -> xopt_set flags ghcFlag
UnsetFlag ghcFlag -> xopt_unset flags ghcFlag
......@@ -101,7 +101,7 @@ setFlags ext = do
-- Create the parse errors.
let noParseErrs = map (("Could not parse: " ++) . unLoc) unrecognized
allWarns = map unLoc warnings ++
allWarns = map unLoc warnings ++
["-package not supported yet" | packageFlags flags /= packageFlags flags']
warnErrs = map ("Warning: " ++) allWarns
return $ noParseErrs ++ warnErrs
......@@ -178,7 +178,7 @@ evalImport imports = do
-- Check whether an import is the same as another import (same module).
importOf :: ImportDecl RdrName -> InteractiveImport -> Bool
importOf _ (IIModule _) = False
importOf imp (IIDecl decl) =
importOf imp (IIDecl decl) =
((==) `on` (unLoc . ideclName)) decl imp && not (ideclQualified decl)
-- Check whether an import is an *implicit* import of something.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment