[GHC] #11301: Using GHC's parser and rendering the results is unreasonably difficult

#11301: Using GHC's parser and rendering the results is unreasonably difficult -------------------------------------+------------------------------------- Reporter: bitemyapp | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: GHC API | Version: 7.10.2 Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- After a couple hours, 30-some browser tabs, and attempting to use ghc- simple to paper over some annoyances with GHC's API (dynFlags/session- stuff, particularly), I gave up. I then opened up haskell-src-exts and found the function I wanted in a minute or two. {{{#!hs import qualified Language.Haskell.Exts.Parser as P parsePls' :: String -> IO () parsePls' s = print $ P.parseExp s }}} This is where I got stuck: {{{ Prelude> parsePls "1 + 1" Too late for parseStaticFlags: call it before runGhc or runGhcT }}} This is the code I was working with before I gave up on GHC: {{{#!hs module ParsePls where import DynFlags import FastString import qualified GHC import qualified GhcMonad as GM import HsSyn import Lexer import Outputable import Parser import RdrName import SrcLoc import StaticFlags import StringBuffer import qualified Language.Haskell.GHC.Simple as S import qualified Language.Haskell.GHC.Simple.Types as ST import System.Environment main :: IO () main = do [expr] <- getArgs parsePls expr parsePls :: String -> IO () parsePls s = do initStaticOpts let sbuf = stringToStringBuffer s srcloc = mkRealSrcLoc (mkFastString s) 1 1 (dynflags, _) <- S.getDynFlagsForConfig ST.defaultConfig -- dynflags <- undefined -- GHC.getSessionDynFlags -- unP :: P a -> PState -> ParseResult a -- mkPState :: DynFlags -> StringBuffer -> RealSrcLoc -> PState -- parseExpression :: P (LHsExpr RdrName) -- type LHsExpr id = Located (HsExpr id) -- Defined in ‘HsExpr’ let parseResult = unP parseExpression (mkPState dynflags sbuf srcloc) case parseResult of POk _ (L _ mdl) -> print $ showPpr dynflags mdl PFailed ss _ -> do putStrLn "Error occurred!" print ss }}} Is there a reason it has to be this difficult? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11301 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11301: Using GHC's parser and rendering the results is unreasonably difficult -------------------------------------+------------------------------------- Reporter: bitemyapp | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: GHC API | Version: 7.10.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Description changed by bitemyapp: Old description:
After a couple hours, 30-some browser tabs, and attempting to use ghc- simple to paper over some annoyances with GHC's API (dynFlags/session- stuff, particularly), I gave up.
I then opened up haskell-src-exts and found the function I wanted in a minute or two.
{{{#!hs
import qualified Language.Haskell.Exts.Parser as P
parsePls' :: String -> IO () parsePls' s = print $ P.parseExp s }}}
This is where I got stuck:
{{{ Prelude> parsePls "1 + 1" Too late for parseStaticFlags: call it before runGhc or runGhcT }}}
This is the code I was working with before I gave up on GHC:
{{{#!hs module ParsePls where
import DynFlags import FastString import qualified GHC import qualified GhcMonad as GM import HsSyn import Lexer import Outputable import Parser import RdrName import SrcLoc import StaticFlags import StringBuffer
import qualified Language.Haskell.GHC.Simple as S import qualified Language.Haskell.GHC.Simple.Types as ST
import System.Environment
main :: IO () main = do [expr] <- getArgs parsePls expr
parsePls :: String -> IO () parsePls s = do initStaticOpts let sbuf = stringToStringBuffer s srcloc = mkRealSrcLoc (mkFastString s) 1 1 (dynflags, _) <- S.getDynFlagsForConfig ST.defaultConfig -- dynflags <- undefined -- GHC.getSessionDynFlags -- unP :: P a -> PState -> ParseResult a
-- mkPState :: DynFlags -> StringBuffer -> RealSrcLoc -> PState -- parseExpression :: P (LHsExpr RdrName) -- type LHsExpr id = Located (HsExpr id) -- Defined in ‘HsExpr’
let parseResult = unP parseExpression (mkPState dynflags sbuf srcloc) case parseResult of POk _ (L _ mdl) -> print $ showPpr dynflags mdl PFailed ss _ -> do putStrLn "Error occurred!" print ss }}}
Is there a reason it has to be this difficult?
New description: After a couple hours, 30-some browser tabs, and attempting to use ghc- simple to paper over some annoyances with GHC's API (dynFlags/session- stuff, particularly), I gave up. I then opened up haskell-src-exts and found the function I wanted in a minute or two. {{{#!hs import qualified Language.Haskell.Exts.Parser as P parsePls' :: String -> IO () parsePls' s = print $ P.parseExp s }}} This is where I got stuck: {{{ Prelude> parsePls "1 + 1" Too late for parseStaticFlags: call it before runGhc or runGhcT }}} This is the code I was working with before I gave up on GHC: {{{#!hs module ParsePls where import DynFlags import FastString import qualified GHC import qualified GhcMonad as GM import HsSyn import Lexer import Outputable import Parser import RdrName import SrcLoc import StaticFlags import StringBuffer import qualified Language.Haskell.GHC.Simple as S import qualified Language.Haskell.GHC.Simple.Types as ST import System.Environment main :: IO () main = do [expr] <- getArgs parsePls expr parsePls :: String -> IO () parsePls s = do initStaticOpts let sbuf = stringToStringBuffer s srcloc = mkRealSrcLoc (mkFastString s) 1 1 (dynflags, _) <- S.getDynFlagsForConfig ST.defaultConfig -- dynflags <- undefined -- GHC.getSessionDynFlags -- unP :: P a -> PState -> ParseResult a -- mkPState :: DynFlags -> StringBuffer -> RealSrcLoc -> PState -- parseExpression :: P (LHsExpr RdrName) -- type LHsExpr id = Located (HsExpr id) -- Defined in ‘HsExpr’ let parseResult = unP parseExpression (mkPState dynflags sbuf srcloc) case parseResult of POk _ (L _ mdl) -> print $ showPpr dynflags mdl PFailed ss _ -> do putStrLn "Error occurred!" print ss }}} Whereas with haskell-src-exts I got: {{{ Prelude> parsePls' "1 + 1" ParseOk (InfixApp (Lit (Int 1)) (QVarOp (UnQual (Symbol "+"))) (Lit (Int 1))) }}} Which was perfectly satisfactory. Is there a reason it has to be this difficult? -- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11301#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11301: Using GHC's parser and rendering the results is unreasonably difficult -------------------------------------+------------------------------------- Reporter: bitemyapp | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: GHC API | Version: 7.10.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by ezyang): Have you considered getting a recent build of GHC 8 and doing this as a frontend plugin? Here's something that works for a recent GHC build: {{{ module Print where import GhcPlugins import DynFlags import FastString import GhcMonad import DriverPhases import HsSyn import Lexer import Outputable import Parser import RdrName import SrcLoc import StaticFlags import StringBuffer import ErrUtils import Control.Monad frontendPlugin :: FrontendPlugin frontendPlugin = defaultFrontendPlugin { frontend = doPrint } doPrint :: [String] -> [(String, Maybe Phase)] -> Ghc () doPrint args [] = error "usage: ghc --frontend Print File.hs" doPrint args fs = do dflags <- getDynFlags forM_ fs $ \(src_filename, _) -> do buf <- liftIO $ hGetStringBuffer src_filename let loc = mkRealSrcLoc (mkFastString src_filename) 1 1 case unP parseModule (mkPState dflags buf loc) of PFailed span err -> liftIO $ throwOneError (mkPlainErrMsg dflags span err) POk pst rdr_module -> liftIO . putStrLn $ showSDoc dflags (ppr rdr_module) }}} Here's how to build and run it; {{{ ghc --make Print.hs -package ghc -dynamic-too ghc --frontend Print Print.hs }}} Evidently, we should expose some sort of function which takes just a file name (rather than a `ModSummary`, which is a little touchy to get). This should be simple enough for someone to add. And I agree: static/dynamic flag initialization is terrible. It would be well worth someone trying to see if we can (finally) get rid of the rest of the static flags. It would also be useful to know what kind of abstracting API people would like to get dumped into the `Ghc` monad with minimum fuss. There IS some fuss involved; for example, it's convenient to have GHC's options parser around so that `DynFlags` are as configurable as they are with GHC. Would love proposals! (Willing to improve it, but it's very unclear what a good interface is supposed to be. One of the reasons I wrote the frontend plugins patch instead.) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11301#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11301: Using GHC's parser and rendering the results is unreasonably difficult -------------------------------------+------------------------------------- Reporter: bitemyapp | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: GHC API | Version: 7.10.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by bitemyapp):
Willing to improve it, but it's very unclear what a good interface is supposed to be.
This is good: http://hackage.haskell.org/package/haskell-src-exts-1.17.1/docs/Language- Haskell-Exts-Parser.html#v:parseExp The code you posted is going to send new people running for the hills (or Rust. or OCaml). There's no documentation, no way of knowing why one would want a frontend plugin or what problem it is solving for us. Googling the error message I got earlier was quite fruitless too. We don't really have to spend a lot of time discussing the API. Just catching up the GHC API's UX to the standard set by haskell-src-exts is sufficient for my purposes. Is there a reason a parser of Haskell source code needs to emit IO? If not, then there should be types like this available: {{{#!hs parseExp :: String -> ParseResult Exp }}} even if there are nittier-grittier underlying functions/facilities available. Further, why doesn't `ParseResult` in the GHC API have a Show instance? Trying to figure out something simple like, "parse this expression from a string and print the parse tree to stdout", with GHC's API is way-way-way harder than it should be and adding "more stuff" that the user has to know-about, care-about, etc. isn't good enough. It also looks like this frontend plugin thing doesn't work for REPL use. This is also not satisfactory. I'm assuming there are serious, difficult technical reasons the API provided by GHC doesn't include a simple "just parse the damn string" function with a Showable ParseResult. Could someone please explain those reasons? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11301#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11301: Using GHC's parser and rendering the results is unreasonably difficult -------------------------------------+------------------------------------- Reporter: bitemyapp | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: GHC API | Version: 7.10.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by mpickering): This is essentially a duplicate of #10961. I agree it should be easier to use the parser and don't think there is a good reason why it isn't pure. FWIW, `ghc-exactprint` provides a bit of a nicer interface around the entry points which you might want to use in the mean time. https://hackage.haskell.org/package/ghc-exactprint-0.5.0.1/docs/Language- Haskell-GHC-ExactPrint-Parsers.html -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11301#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11301: Using GHC's parser and rendering the results is unreasonably difficult
-------------------------------------+-------------------------------------
Reporter: bitemyapp | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: GHC API | Version: 7.10.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Ben Gamari

#11301: Using GHC's parser and rendering the results is unreasonably difficult -------------------------------------+------------------------------------- Reporter: bitemyapp | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: GHC API | Version: 7.10.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by bgamari): The above patch has decoupled `DynFlags` from `ParserFlags`: {{{#!hs mkParserFlags' :: EnumSet WarningFlag -- ^ warnings flags enabled -> EnumSet LangExt.Extension -- ^ permitted language extensions enabled -> UnitId -- ^ key of package currently being compiled -> Bool -- ^ are safe imports on? -> Bool -- ^ keeping Haddock comment tokens -> Bool -- ^ keep regular comment tokens -> ParserFlags }}} However, I suspect there is still more to be done here. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11301#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11301: Using GHC's parser and rendering the results is unreasonably difficult
-------------------------------------+-------------------------------------
Reporter: bitemyapp | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: GHC API | Version: 7.10.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Ben Gamari
participants (1)
-
GHC