Library for generating Haskell source files and code fragments.
Go to file
Ian-Woo Kim 1f0ccfc5a2
Make ghc-source-gen compatible with GHC 9.10 (#117)
* try to adjust src/GHC/SourceGen/Syntax/Internal.hs for GHC 9.10 API

* fix parPat

* update GHC.SourceGen.Module

* fix GHC.SourceGen.Lit

* GHC.SourceGen.Expr.Internal fixed

* adjust HsMatchContext'
fix GHC.SourceGen.Binds.Internal

* fix GHC.SourceGen.Type.Internal: fix (parTy, patSigType)

* fix most of GHC.SourceGen.Type, except forall'

* resolve forall'

* fix GHC.SourceGen.Pat

* GHC.SourceGen.Overloaded!

* some success in GHC.SourceGen.Decl. but not for type family yet

* fix GHC.SourceGen.Binds

* GHC.SourceGen.Expr!

* Finally, GHC.SourceGen.Decl! ghc-source-gen lib is built with GHC 9.10!

* fix upper bound

* bump

* add 9.10 to CI.

* stack-9.10.yaml

* fix errata

* fix a few mistakes in CPPs

* update package.yaml

* Update stack-9.10.yaml

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>

* fix a mistake in the recent commit.

* one day rollback

* try extra-deps

* another try

* yet another try with allow-newer

* bump CI

* add os-string-2.0.2

* Update src/GHC/SourceGen/Syntax/Internal.hs

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>

* Update src/GHC/SourceGen/Overloaded.hs

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>

* Update src/GHC/SourceGen/Module.hs

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>

* Update src/GHC/SourceGen/Decl.hs

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>

* minimize some changes

* Update src/GHC/SourceGen/Decl.hs

eqnBndrs

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>

* remove redundant toHsDeriving

* factor out mkDataDefnCon

* resolve name conflict

---------

Co-authored-by: Greg Steuck <blackgnezdo@gmail.com>
2024-11-05 16:54:25 -08:00
.github/workflows Make ghc-source-gen compatible with GHC 9.10 (#117) 2024-11-05 16:54:25 -08:00
compat/GHC Complete GHC 9 support and CI 2021-08-12 13:23:54 +01:00
compat-8.10/GHC Complete GHC 9 support and CI 2021-08-12 13:23:54 +01:00
ghc-show-ast Support GHC 9.6 (#103) 2023-12-20 11:17:57 -08:00
src/GHC Make ghc-source-gen compatible with GHC 9.10 (#117) 2024-11-05 16:54:25 -08:00
tests Complete GHC 9 support and CI 2021-08-12 13:23:54 +01:00
.gitignore Support GHC 9.8 (#108) (#112) 2024-04-01 08:56:12 -07:00
ChangeLog.md Release notes for 0.4.5.0 2024-04-07 10:41:04 -07:00
CONTRIBUTING.md Initial commit. 2019-07-09 13:58:46 -07:00
ghc-source-gen.cabal Make ghc-source-gen compatible with GHC 9.10 (#117) 2024-11-05 16:54:25 -08:00
LICENSE Initial commit. 2019-07-09 13:58:46 -07:00
package.yaml Make ghc-source-gen compatible with GHC 9.10 (#117) 2024-11-05 16:54:25 -08:00
README.md Fix the example in the readme (#101) 2022-09-06 20:20:35 -07:00
Setup.hs Initial commit. 2019-07-09 13:58:46 -07:00
stack-9.0.yaml Support GHC 9.6 (#103) 2023-12-20 11:17:57 -08:00
stack-9.2.yaml Support GHC 9.6 (#103) 2023-12-20 11:17:57 -08:00
stack-9.4.yaml Support GHC 9.6 (#103) 2023-12-20 11:17:57 -08:00
stack-9.6.yaml Trigger CI on pull_requests 2024-04-07 10:41:04 -07:00
stack-9.8.yaml Trigger CI on pull_requests 2024-04-07 10:41:04 -07:00
stack-9.10.yaml Make ghc-source-gen compatible with GHC 9.10 (#117) 2024-11-05 16:54:25 -08:00
stack.yaml Pin newer versions of stackage, update CI versions (#100) 2022-08-21 17:24:38 -07:00

ghc-source-gen

ghc-source-gen is a Haskell library for generating Haskell source files and code fragments. It uses GHC's library API to support the latest syntax, and provides a simple, consistent interface across several major versions of GHC.

To get started, take a look at the example below, or check out the GHC.SourceGen module.

This package is not an officially supported Google product.

Example

The following example creates a module that defines the const function:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
import GHC.SourceGen
import GHC.Paths (libdir)
import GHC (runGhc)

constModule :: HsModule'
constModule =
    module' (Just "Const") (Just [var "const"]) []
        [ typeSig "const" $ a --> b --> a
        , funBind "const" $ match [x, wildP] x
        ]
  where
    a = var "a"
    b = var "b"
    x = bvar "x"

main = runGhc (Just libdir) $ putPpr constModule

The output of that program is:

module Const (
        const
    ) where
const :: a -> b -> a
const x _ = x

Comparison with the GHC API

The raw GHC API has several complexities that ghc-source-gen simplifies for the purpose of source code generation.

Backwards-compatibility

ghc-source-gen provides the same API across several versions of GHC. Code written with ghc-source-gen should compile unchanged on each of those versions.

Currently, this library supports GHC versions 8.2, 8.4, 8.6 and 8.8.

One caveat: ghc-source-gen supports some forms of syntax which are not implemented by all of those GHC versions. For example, the DerivingVia extension is only implemented in ghc >= 8.6. When built on older versions of GHC, ghc-source-gen will omit functions for constructing that syntax (for example: GHC.SourceGen.Decl.derivingVia). We will also tag any such function with a note in its Haddock documentation.

Less verbose types and construction functions

The datatypes that GHC uses to represent Haskell syntax change their representation at different stages of the compilation: for example, parsing, renaming, or type-checking. That data transformation provides type safety and a uniform structure across the phases. However, it also adds unnecessary complexity to the task of source code generation.

ghc-source-gen aims to provide a simple interface by creating data types as GHC would represent them immediately after its parsing step. For example, ghc >= 8.4 uses a type parameter p in its syntax types: HsExpr p for expressions, HsDecl p for declarations, etc. ghc-source-gen defines type synonyms for them:

type HsExpr' = HsExpr GhcPs
type HsDecl' = HsDecl GhcPs
type HsType' = HsType GhcPs
-- etc.

Furthermore, most constructors take an extra "extension" field which can contain different information in different stages, influenced by the parameter p. In almost all cases, after the parsing step that field is the trivial type data NoExt = NoExt. (For more details, see the Trees that Grow paper. GHC versions earlier than 8.4 used a similar PlaceHolder type.). This extra data makes code generation more verbose.

ghc-source-gen automatically sets the NoExt value (or equivalent) for the terms that it generates, hiding that detail from its external API. It also sets and hides other fields that are irrelevant to parsing or pretty-printing, such as simplifier ticks.

Source Locations

GHC carefully tracks the source location of (nearly) every node in the AST. That information is very useful for error reporting. However, it would be too verbose to set it explicitly for each individual node during code generation. Furthermore, GHC doesn't use the source location when pretty-printing its output, which is ghc-source-gen's main use case.

Currently, ghc-source-gen gives to each node it generates a trivial location without an explicit line or column.

Parentheses

GHC represents parentheses explicitly in its syntax tree, so that it can print code exactly as it was parsed. Unfortunately, this means that its pretty-printing code expects those parentheses to be present, and outputs incorrect source code if they are missing. ghc-source-gen adds parentheses automatically in the code that it generates.

For example, consider a simplified expression syntax:

data Expr
    = VarE String      -- ^ Variables
    | App Expr Expr    -- ^ Function application
    | Paren Expr Expr  -- ^ Parentheses

Then GHC would pretty-print as "f (g x)" the tree

App (VarE "f") $ Paren $ App (VarE "g") (VarE "h")

But without the explicit parenthesis, it would pretty-print as "f g x":

App (VarE "f") $ App (VarE "g") (VarE "h")

which misrepresents the precedence between the two function applications.

ghc-source-gen resolves this issue by inserting parentheses automatically, and only when necessary. In the expression

var "f" @@ (var "g" @@ var "h")

it inserts a parenthesis automatically so that the result pretty-prints to "f (g x)" as expected.

GHC uses a similar approach internally itself. For more discussion, see tickets 14289 and 15738.