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

Implement pragma handling

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