Brandon Chinn pushed to branch wip/interpolated-strings at Glasgow Haskell Compiler / GHC

Commits:

29 changed files:

Changes:

  • compiler/GHC/Builtin/Names.hs
    ... ... @@ -286,7 +286,7 @@ basicKnownKeyNames
    286 286
             fromStringName,
    
    287 287
     
    
    288 288
             -- Interpolated strings
    
    289
    -        fromBuilderName, toBuilderName, interpolateName,
    
    289
    +        interpolateName,
    
    290 290
     
    
    291 291
             -- Enum stuff
    
    292 292
             enumFromName, enumFromThenName,
    
    ... ... @@ -1123,9 +1123,7 @@ minusName = varQual gHC_INTERNAL_NUM (fsLit "-") minusClassOpK
    1123 1123
     negateName        = varQual gHC_INTERNAL_NUM (fsLit "negate")      negateClassOpKey
    
    1124 1124
     
    
    1125 1125
     -- Module GHC.Internal.Data.String.Interpolate
    
    1126
    -toBuilderName, fromBuilderName, interpolateName :: Name
    
    1127
    -toBuilderName   = varQual gHC_INTERNAL_DATA_STRING_INTERPOLATE (fsLit "toBuilder") toBuilderKey
    
    1128
    -fromBuilderName = varQual gHC_INTERNAL_DATA_STRING_INTERPOLATE (fsLit "fromBuilder") fromBuilderKey
    
    1126
    +interpolateName :: Name
    
    1129 1127
     interpolateName = varQual gHC_INTERNAL_DATA_STRING_INTERPOLATE (fsLit "interpolate") interpolateKey
    
    1130 1128
     
    
    1131 1129
     ---------------------------------
    
    ... ... @@ -2502,9 +2500,7 @@ proxyHashKey :: Unique
    2502 2500
     proxyHashKey = mkPreludeMiscIdUnique 502
    
    2503 2501
     
    
    2504 2502
     -- String interpolation
    
    2505
    -toBuilderKey, fromBuilderKey, interpolateKey :: Unique
    
    2506
    -toBuilderKey   = mkPreludeMiscIdUnique 574
    
    2507
    -fromBuilderKey = mkPreludeMiscIdUnique 575
    
    2503
    +interpolateKey :: Unique
    
    2508 2504
     interpolateKey = mkPreludeMiscIdUnique 576
    
    2509 2505
     
    
    2510 2506
     ---------------- Template Haskell -------------------
    

  • compiler/GHC/Driver/Flags.hs
    ... ... @@ -260,6 +260,7 @@ extensionName = \case
    260 260
       LangExt.ListTuplePuns -> "ListTuplePuns"
    
    261 261
       LangExt.MultilineStrings -> "MultilineStrings"
    
    262 262
       LangExt.StringInterpolation -> "StringInterpolation"
    
    263
    +  LangExt.QualifiedLiterals -> "QualifiedLiterals"
    
    263 264
       LangExt.ExplicitLevelImports -> "ExplicitLevelImports"
    
    264 265
       LangExt.ImplicitStagePersistence -> "ImplicitStagePersistence"
    
    265 266
     
    

  • compiler/GHC/Hs/Expr.hs
    ... ... @@ -868,8 +868,8 @@ ppr_expr (HsOverLabel s l) = case ghcPass @p of
    868 868
     ppr_expr (HsLit _ lit)       = ppr lit
    
    869 869
     ppr_expr (HsOverLit _ lit)   = ppr lit
    
    870 870
     
    
    871
    -ppr_expr (HsInterString _ strType parts) =
    
    872
    -  char 's' <> delim <> hcat (map pprInterPart parts) <> delim
    
    871
    +ppr_expr (HsInterString _ mQualMod strType parts) =
    
    872
    +  prefix <> delim <> hcat (map pprInterPart parts) <> delim
    
    873 873
       where
    
    874 874
         pprInterPart = \case
    
    875 875
           HsInterStringRaw st s ->
    
    ... ... @@ -880,6 +880,11 @@ ppr_expr (HsInterString _ strType parts) =
    880 880
               (HsStringTypeMulti, NoSourceText) -> pprHsStringMulti' (unpackFS s)
    
    881 881
           HsInterStringExpr _ expr -> text "${" <> ppr_lexpr expr <> text "}"
    
    882 882
     
    
    883
    +    prefix =
    
    884
    +      case mQualMod of
    
    885
    +        Nothing -> char 's'
    
    886
    +        Just qualMod -> ppr qualMod <> char '.'
    
    887
    +
    
    883 888
         delim =
    
    884 889
           case strType of
    
    885 890
             HsStringTypeSingle -> char '"'
    

  • compiler/GHC/Hs/Syn/Type.hs
    ... ... @@ -106,7 +106,7 @@ hsExprType (HsOverLabel v _) = dataConCantHappen v
    106 106
     hsExprType (HsIPVar v _) = dataConCantHappen v
    
    107 107
     hsExprType (HsOverLit _ lit) = overLitType lit
    
    108 108
     hsExprType (HsLit _ lit) = hsLitType lit
    
    109
    -hsExprType (HsInterString _ _ _) = stringTy -- TODO: handle OverloadedStrings
    
    109
    +hsExprType (HsInterString _ _ _ _) = stringTy -- TODO: handle OverloadedStrings + QualifiedLiterals
    
    110 110
     hsExprType (HsLam _ _ (MG { mg_ext = match_group })) = matchGroupTcType match_group
    
    111 111
     hsExprType (HsApp _ f _) = funResultTy $ lhsExprType f
    
    112 112
     hsExprType (HsAppType x f _) = piResultTy (lhsExprType f) x
    

  • compiler/GHC/HsToCore/Expr.hs
    ... ... @@ -554,7 +554,7 @@ dsExpr (HsOverLabel x _) = dataConCantHappen x
    554 554
     dsExpr (OpApp x _ _ _)    = dataConCantHappen x
    
    555 555
     dsExpr (SectionL x _ _)   = dataConCantHappen x
    
    556 556
     dsExpr (SectionR x _ _)   = dataConCantHappen x
    
    557
    -dsExpr (HsInterString x _ _) = dataConCantHappen x
    
    557
    +dsExpr (HsInterString x _ _ _) = dataConCantHappen x
    
    558 558
     
    
    559 559
     
    
    560 560
     {- *********************************************************************
    

  • compiler/GHC/HsToCore/Quote.hs
    ... ... @@ -1576,11 +1576,11 @@ repE (HsOverLabel _ s) = repOverLabel s
    1576 1576
             -- HsOverlit can definitely occur
    
    1577 1577
     repE (HsOverLit _ l) = do { a <- repOverloadedLiteral l; repLit a }
    
    1578 1578
     repE (HsLit _ l)     = do { a <- repLiteral l;           repLit a }
    
    1579
    -repE (HsInterString _ _ parts) = do
    
    1579
    +repE (HsInterString _ mQualMod _ parts) = do
    
    1580 1580
       parts' <- forM parts $ \case
    
    1581 1581
         HsInterStringRaw _ s -> repInterStringRaw =<< coreStringLit s
    
    1582 1582
         HsInterStringExpr _ e -> repInterStringExp =<< repLE e
    
    1583
    -  repInterString =<< coreListM interStringPartName parts'
    
    1583
    +  repInterString mQualMod =<< coreListM interStringPartName parts'
    
    1584 1584
     repE (HsLam _ LamSingle (MG { mg_alts = L _ [m] })) = repLambda m
    
    1585 1585
     repE e@(HsLam _ LamSingle (MG { mg_alts = L _ _ })) = pprPanic "repE: HsLam with multiple alternatives" (ppr e)
    
    1586 1586
     repE (HsLam _ LamCase (MG { mg_alts = L _ ms }))
    
    ... ... @@ -2565,16 +2565,17 @@ repMDoE = repDoBlock mdoEName
    2565 2565
     
    
    2566 2566
     repDoBlock :: Name -> Maybe ModuleName -> Core [(M TH.Stmt)] -> MetaM (Core (M TH.Exp))
    
    2567 2567
     repDoBlock doName maybeModName (MkC ss) = do
    
    2568
    -    MkC coreModName <- coreModNameM
    
    2569
    -    rep2 doName [coreModName, ss]
    
    2570
    -  where
    
    2571
    -    coreModNameM :: MetaM (Core (Maybe TH.ModName))
    
    2572
    -    coreModNameM = case maybeModName of
    
    2573
    -      Just m -> do
    
    2574
    -        MkC s <- coreStringLit (moduleNameFS m)
    
    2575
    -        mName <- rep2_nw mkModNameName [s]
    
    2576
    -        coreJust modNameTyConName mName
    
    2577
    -      _ -> coreNothing modNameTyConName
    
    2568
    +    MkC mCoreModName <- repMaybeModName maybeModName
    
    2569
    +    rep2 doName [mCoreModName, ss]
    
    2570
    +
    
    2571
    +repMaybeModName :: Maybe ModuleName -> MetaM (Core (Maybe TH.ModName))
    
    2572
    +repMaybeModName = \case
    
    2573
    +  Just m -> do
    
    2574
    +    MkC s <- coreStringLit (moduleNameFS m)
    
    2575
    +    mName <- rep2_nw mkModNameName [s]
    
    2576
    +    coreJust modNameTyConName mName
    
    2577
    +  Nothing ->
    
    2578
    +    coreNothing modNameTyConName
    
    2578 2579
     
    
    2579 2580
     repComp :: Core [(M TH.Stmt)] -> MetaM (Core (M TH.Exp))
    
    2580 2581
     repComp (MkC ss) = rep2 compEName [ss]
    
    ... ... @@ -2663,8 +2664,10 @@ repClause :: Core [(M TH.Pat)] -> Core (M TH.Body) -> Core [(M TH.Dec)] -> MetaM
    2663 2664
     repClause (MkC ps) (MkC bod) (MkC ds) = rep2 clauseName [ps, bod, ds]
    
    2664 2665
     
    
    2665 2666
     -------------- Interpolated strings -----------------------------
    
    2666
    -repInterString :: Core [M TH.InterStringPart] -> MetaM (Core (M TH.Exp))
    
    2667
    -repInterString (MkC parts) = rep2 interStringEName [parts]
    
    2667
    +repInterString :: Maybe ModuleName -> Core [M TH.InterStringPart] -> MetaM (Core (M TH.Exp))
    
    2668
    +repInterString mQualMod (MkC parts) = do
    
    2669
    +  MkC mCoreModName <- repMaybeModName mQualMod
    
    2670
    +  rep2 interStringEName [mCoreModName, parts]
    
    2668 2671
     
    
    2669 2672
     repInterStringRaw :: Core String -> MetaM (Core (M TH.InterStringPart))
    
    2670 2673
     repInterStringRaw (MkC s) = rep2 interStringRawName [s]
    

  • compiler/GHC/HsToCore/Ticks.hs
    ... ... @@ -477,11 +477,12 @@ addTickHsExpr e@(HsIPVar {}) = return e
    477 477
     addTickHsExpr e@(HsOverLit {})          = return e
    
    478 478
     addTickHsExpr e@(HsOverLabel{})         = return e
    
    479 479
     addTickHsExpr e@(HsLit {})              = return e
    
    480
    -addTickHsExpr (HsInterString x ty parts) = do
    
    480
    +addTickHsExpr (HsInterString x mQualMod ty parts) = do
    
    481
    +  -- TODO: should we add ticks for qualified literals?
    
    481 482
       parts' <- forM parts $ \case
    
    482 483
         part@(HsInterStringRaw {}) -> return part
    
    483 484
         HsInterStringExpr x e -> HsInterStringExpr x <$> addTickLHsExpr e
    
    484
    -  return $ HsInterString x ty parts'
    
    485
    +  return $ HsInterString x mQualMod ty parts'
    
    485 486
     addTickHsExpr e@(HsEmbTy {})            = return e
    
    486 487
     addTickHsExpr e@(HsHole {})             = return e
    
    487 488
     addTickHsExpr e@(HsQual {})             = return e
    

  • compiler/GHC/Iface/Ext/Ast.hs
    ... ... @@ -1198,7 +1198,7 @@ instance HiePass p => ToHie (LocatedA (HsExpr (GhcPass p))) where
    1198 1198
             [ toHie (L mspan o)
    
    1199 1199
             ]
    
    1200 1200
           HsLit _ _ -> []
    
    1201
    -      HsInterString _ _ parts ->
    
    1201
    +      HsInterString _ _ _ parts ->
    
    1202 1202
             [ toHie expr
    
    1203 1203
             | HsInterStringExpr _ expr <- parts
    
    1204 1204
             ]
    

  • compiler/GHC/Parser.y
    ... ... @@ -736,9 +736,9 @@ are the most common patterns, rewritten as regular expressions for clarity:
    736 736
      CHAR           { L _ (ITchar   _ _) }
    
    737 737
      STRING         { L _ (ITstring _ StringTypeSingle _) }
    
    738 738
      STRING_MULTI   { L _ (ITstring _ StringTypeMulti _) }
    
    739
    - STRING_INTER_BEGIN       { L _ (ITstringInterBegin StringTypeSingle) }
    
    739
    + STRING_INTER_BEGIN       { L _ (ITstringInterBegin _ StringTypeSingle) }
    
    740 740
      STRING_INTER_END         { L _ (ITstringInterEnd   StringTypeSingle) }
    
    741
    - STRING_INTER_MULTI_BEGIN { L _ (ITstringInterBegin StringTypeMulti) }
    
    741
    + STRING_INTER_MULTI_BEGIN { L _ (ITstringInterBegin _ StringTypeMulti) }
    
    742 742
      STRING_INTER_MULTI_END   { L _ (ITstringInterEnd   StringTypeMulti) }
    
    743 743
      STRING_INTER_RAW         { L _ (ITstringInterRaw _ _) }
    
    744 744
      STRING_INTER_EXP_OPEN    { L _ ITstringInterExpOpen }
    
    ... ... @@ -4365,7 +4365,7 @@ processStringInter ::
    4365 4365
     processStringInter strType tokBegin parts tokEnd = do
    
    4366 4366
       parts' <- mapM mkInterStringPartPV $ processRawLexedStrings parts
    
    4367 4367
       ams1 (L (comb2 tokBegin tokEnd) ()) $
    
    4368
    -    HsInterString noExtField strType parts'
    
    4368
    +    HsInterString noExtField mQualMod strType parts'
    
    4369 4369
       where
    
    4370 4370
         processRawLexedStrings ::
    
    4371 4371
           [Either (SourceText, RawLexedString) ECP] ->
    
    ... ... @@ -4379,6 +4379,10 @@ processStringInter strType tokBegin parts tokEnd = do
    4379 4379
           Left (src, s) -> pure $ HsInterStringRaw src (fsLit s)
    
    4380 4380
           Right (ECP e) -> HsInterStringExpr noExtField <$> runPV e
    
    4381 4381
     
    
    4382
    +    mQualMod =
    
    4383
    +      let L _ (ITstringInterBegin mMod _) = tokBegin
    
    4384
    +       in mkModuleNameFS <$> mMod
    
    4385
    +
    
    4382 4386
     -- Utilities for combining source spans
    
    4383 4387
     comb2 :: (HasLoc a, HasLoc b) => a -> b -> SrcSpan
    
    4384 4388
     comb2 !a !b = combineHasLocs a b
    

  • compiler/GHC/Parser/Lexer.x
    ... ... @@ -632,6 +632,7 @@ $unigraphic / { isSmartQuote } { smart_quote_error }
    632 632
     -- See Note [Parsing interpolated strings] and Note [Lexing interpolated strings]
    
    633 633
     <0,string_inter> {
    
    634 634
       s \" / { ifExtension StringInterpolationBit } { string_inter_begin }
    
    635
    +  @qual \" / { ifExtension QualifiedLiteralsBit } { string_inter_begin }
    
    635 636
       -- TODO(bchinn): interpolated multiline strings
    
    636 637
     }
    
    637 638
     
    
    ... ... @@ -930,7 +931,9 @@ data Token
    930 931
       | ITstring SourceText StringType FastString -- Note [Literal source text] in "GHC.Types.SourceText"
    
    931 932
     
    
    932 933
       -- See Note [Parsing interpolated strings]
    
    933
    -  | ITstringInterBegin    StringType
    
    934
    +  | ITstringInterBegin
    
    935
    +      (Maybe FastString) -- Module name, if using QualifiedLiterals
    
    936
    +      StringType         -- Single-line or multiline interpolated string?
    
    934 937
       | ITstringInterRaw      SourceText RawLexedString -- Note [Literal source text] in "GHC.Types.SourceText"
    
    935 938
       | ITstringInterExpOpen
    
    936 939
       | ITstringInterExpClose
    
    ... ... @@ -2196,9 +2199,14 @@ tok_string span buf len _buf2 = do
    2196 2199
         endsInHash = currentChar (offsetBytes (len - 1) buf) == '#'
    
    2197 2200
     
    
    2198 2201
     string_inter_begin :: Action
    
    2199
    -string_inter_begin span _ _ _ = do
    
    2202
    +string_inter_begin span buf len _ = do
    
    2200 2203
       pushLexState string_inter_content
    
    2201
    -  pure $ L span (ITstringInterBegin StringTypeSingle)
    
    2204
    +  let mQualMod
    
    2205
    +        | len == 2 = Nothing
    
    2206
    +        | otherwise =
    
    2207
    +            let (qualMod, _) = splitQualName buf len False
    
    2208
    +            in Just qualMod
    
    2209
    +  pure $ L span (ITstringInterBegin mQualMod StringTypeSingle)
    
    2202 2210
     
    
    2203 2211
     string_inter_content_action :: Action
    
    2204 2212
     string_inter_content_action span_init buf_init _ _ = go $ AI (psSpanStart span_init) buf_init
    
    ... ... @@ -2835,6 +2843,7 @@ data ExtBits
    2835 2843
       | RequiredTypeArgumentsBit
    
    2836 2844
       | MultilineStringsBit
    
    2837 2845
       | StringInterpolationBit
    
    2846
    +  | QualifiedLiteralsBit
    
    2838 2847
       | LevelImportsBit
    
    2839 2848
     
    
    2840 2849
       -- Flags that are updated once parsing starts
    
    ... ... @@ -2920,6 +2929,7 @@ mkParserOpts extensionFlags diag_opts
    2920 2929
           .|. RequiredTypeArgumentsBit    `xoptBit` LangExt.RequiredTypeArguments
    
    2921 2930
           .|. MultilineStringsBit         `xoptBit` LangExt.MultilineStrings
    
    2922 2931
           .|. StringInterpolationBit      `xoptBit` LangExt.StringInterpolation
    
    2932
    +      .|. QualifiedLiteralsBit      `xoptBit` LangExt.QualifiedLiterals
    
    2923 2933
           .|. LevelImportsBit             `xoptBit` LangExt.ExplicitLevelImports
    
    2924 2934
         optBits =
    
    2925 2935
               HaddockBit        `setBitIf` isHaddock
    

  • compiler/GHC/Parser/String.hs
    ... ... @@ -339,19 +339,19 @@ Interpolated strings are parsed in the following manner:
    339 339
     
    
    340 340
       and outputs the following tokens:
    
    341 341
     
    
    342
    -    [ ITstringInterBegin    src StringTypeSingle
    
    342
    +    [ ITstringInterBegin    Nothing StringTypeSingle
    
    343 343
         , ITstringInterRaw      src "Hello "
    
    344
    -    , ITstringInterExpOpen  src
    
    344
    +    , ITstringInterExpOpen
    
    345 345
         , ITqvarid                  ("Text.toUpper", "name")
    
    346 346
         , ITvarid                   "name"
    
    347
    -    , ITstringInterExpClose src
    
    347
    +    , ITstringInterExpClose
    
    348 348
         , ITstringInterRaw      src "!"
    
    349
    -    , ITstringInterEnd      src StringTypeSingle
    
    349
    +    , ITstringInterEnd          StringTypeSingle
    
    350 350
         ]
    
    351 351
     
    
    352 352
     2. The parser will then parse the tokens into the following HsExpr:
    
    353 353
     
    
    354
    -    HsInterString ext
    
    354
    +    HsInterString ext Nothing StringTypeSingle
    
    355 355
           [ HsInterStringRaw ext "Hello "
    
    356 356
           , HsInterStringExp ext $
    
    357 357
               HsApp ext
    

  • compiler/GHC/Rename/Expr.hs
    ... ... @@ -385,9 +385,9 @@ rnExpr (HsLit x lit)
    385 385
     rnExpr (HsOverLit x lit)
    
    386 386
       = rnOverLit x lit
    
    387 387
     
    
    388
    -rnExpr (HsInterString _ strType parts) = do
    
    388
    +rnExpr (HsInterString _ mQualMod strType parts) = do
    
    389 389
       (parts', fvs1) <- unzip <$> mapM rnInterStringPart parts
    
    390
    -  (expr, fvs2) <- rewriteInterString strType parts'
    
    390
    +  (expr, fvs2) <- rewriteInterString mQualMod strType parts'
    
    391 391
       pure (expr, plusFVs fvs1 `plusFV` fvs2)
    
    392 392
       where
    
    393 393
         rnInterStringPart = \case
    

  • compiler/GHC/Rename/String.hs
    1 1
     {-# LANGUAGE LambdaCase #-}
    
    2
    +{-# LANGUAGE MultiWayIf #-}
    
    3
    +{-# LANGUAGE RecordWildCards #-}
    
    2 4
     
    
    3 5
     module GHC.Rename.String (
    
    4 6
       rewriteInterString,
    
    ... ... @@ -7,24 +9,24 @@ module GHC.Rename.String (
    7 9
     import GHC.Prelude
    
    8 10
     
    
    9 11
     import GHC.Builtin.Names (
    
    10
    -  fromBuilderName,
    
    11 12
       interpolateName,
    
    12
    -  mappendName,
    
    13
    -  memptyName,
    
    14
    -  toBuilderName,
    
    13
    +  mconcatName,
    
    15 14
      )
    
    16 15
     import GHC.Builtin.Types (stringTyConName)
    
    17 16
     import GHC.Data.FastString (fsLit, unpackFS)
    
    18 17
     import GHC.Hs
    
    19 18
     import qualified GHC.LanguageExtensions as LangExt
    
    19
    +import GHC.Rename.Env (lookupOccRn)
    
    20 20
     import GHC.Rename.Pat (rnOverLit)
    
    21
    +import GHC.Tc.Errors.Types (WhatLooking (WL_None))
    
    21 22
     import GHC.Tc.Utils.Monad
    
    23
    +import GHC.Types.Name (Name)
    
    24
    +import GHC.Types.Name.Occurrence (mkVarOcc)
    
    25
    +import GHC.Types.Name.Reader (mkRdrQual)
    
    22 26
     import GHC.Types.Name.Set (FreeVars, emptyFVs, plusFVs)
    
    23 27
     import GHC.Types.SourceText (SourceText (..))
    
    24 28
     import GHC.Types.SrcLoc (unLoc)
    
    25 29
     
    
    26
    -import qualified Data.List.NonEmpty as NE
    
    27
    -
    
    28 30
     {- Note [Desugaring interpolated strings]
    
    29 31
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    30 32
     
    
    ... ... @@ -34,7 +36,7 @@ Cross-references:
    34 36
     
    
    35 37
     Interpolated strings are represented with the following HsExpr tree:
    
    36 38
     
    
    37
    -    HsInterString ext
    
    39
    +    HsInterString ext mQualMod strType
    
    38 40
           [ HsInterStringRaw ext "Hello "
    
    39 41
           , HsInterStringExp ext $
    
    40 42
               HsApp ext
    
    ... ... @@ -45,12 +47,19 @@ Interpolated strings are represented with the following HsExpr tree:
    45 47
     
    
    46 48
     We'll expand this during the renamer phase into the equivalent of:
    
    47 49
     
    
    48
    -    import GHC.Internal.Data.String.Interpolate
    
    50
    +    mconcat
    
    51
    +      [ fromString "Hello "
    
    52
    +      , interpolate (Text.toUpper name)
    
    53
    +      , fromString "!"
    
    54
    +      ]
    
    49 55
     
    
    50
    -    fromBuilder $
    
    51
    -         toBuilder "Hello "
    
    52
    -      <> interpolate (Text.toUpper name)
    
    53
    -      <> toBuilder "!"
    
    56
    +If using QualifiedLiterals (mQualMod is Just), expand to:
    
    57
    +
    
    58
    +    ModName.fromParts
    
    59
    +      [ ModName.fromString "Hello "
    
    60
    +      , ModName.interpolate (Text.toUpper name)
    
    61
    +      , ModName.fromString "!"
    
    62
    +      ]
    
    54 63
     
    
    55 64
     We're doing this in the renamer phase so that the expanded expression
    
    56 65
     can be typechecked as usual, without any additional work.
    
    ... ... @@ -61,42 +70,77 @@ can be typechecked as usual, without any additional work.
    61 70
     -- necessary.
    
    62 71
     --
    
    63 72
     -- TODO(bchinn): allow -XRebindableSyntax -- lookupSyntaxName
    
    64
    -rewriteInterString :: HsStringType -> [HsInterStringPart GhcRn] -> RnM (HsExpr GhcRn, FreeVars)
    
    65
    -rewriteInterString strType parts = do
    
    73
    +rewriteInterString ::
    
    74
    +  Maybe ModuleName
    
    75
    +  -> HsStringType
    
    76
    +  -> [HsInterStringPart GhcRn]
    
    77
    +  -> RnM (HsExpr GhcRn, FreeVars)
    
    78
    +rewriteInterString mQualMod strType parts = do
    
    66 79
       overloaded <- xoptM LangExt.OverloadedStrings
    
    67
    -  (parts', fvs) <- unzip <$> mapM (rewritePart overloaded) parts
    
    80
    +  mQualNames <- traverse lookupQualifiedLiteralStringsNames mQualMod
    
    81
    +  rewriteInterStringImpl overloaded mQualNames strType parts
    
    82
    +
    
    83
    +rewriteInterStringImpl ::
    
    84
    +  Bool
    
    85
    +  -> Maybe QualifiedLiteralStringsNames
    
    86
    +  -> HsStringType
    
    87
    +  -> [HsInterStringPart GhcRn]
    
    88
    +  -> RnM (HsExpr GhcRn, FreeVars)
    
    89
    +rewriteInterStringImpl overloaded mQualNames strType parts = do
    
    90
    +  (parts', fvs) <- unzip <$> mapM rewritePart parts
    
    68 91
       let expr =
    
    69
    -        (if overloaded then id else addSig) . nlHsApp (nlHsVar fromBuilderName) $
    
    70
    -          maybe (nlHsVar memptyName) (foldr1 appendParts) (NE.nonEmpty parts')
    
    92
    +        addSig
    
    93
    +        . (nlHsApp $ nlHsVar $ maybe mconcatName qualFromParts mQualNames)
    
    94
    +        $ noLocA (ExplicitList noExtField parts')
    
    71 95
       pure (unLoc expr, plusFVs fvs)
    
    72 96
       where
    
    73
    -    appendParts l r = nlHsApps mappendName [l, r]
    
    74
    -    rewritePart overloaded = \case
    
    75
    -      HsInterStringRaw _ s -> do
    
    76
    -        (lit, fvs) <- mkStringLit overloaded s
    
    77
    -        pure (nlHsApps toBuilderName [lit], fvs)
    
    78
    -      HsInterStringExpr _ e ->
    
    79
    -        pure (nlHsApps interpolateName [e], emptyFVs)
    
    80
    -
    
    81
    -    -- Add ":: String" to the given expression
    
    82
    -    addSig e =
    
    83
    -      noLocA . ExprWithTySig noExtField e $
    
    84
    -        HsWC
    
    85
    -          { hswc_ext = []
    
    86
    -          , hswc_body =
    
    87
    -              noLocA
    
    88
    -                HsSig
    
    89
    -                  { sig_ext   = noExtField
    
    90
    -                  , sig_bndrs = HsOuterImplicit []
    
    91
    -                  , sig_body  = nlHsTyVar NotPromoted stringTyConName
    
    92
    -                  }
    
    93
    -          }
    
    94
    -
    
    95
    -    mkStringLit overloaded s = do
    
    97
    +    rewritePart = \case
    
    98
    +      HsInterStringRaw _ s -> mkStringLit s
    
    99
    +      HsInterStringExpr _ e -> do
    
    100
    +        let interpolateName' = maybe interpolateName qualInterpolate mQualNames
    
    101
    +        pure (nlHsApp (nlHsVar interpolateName') e, emptyFVs)
    
    102
    +
    
    103
    +    addSig e
    
    104
    +      | Just _ <- mQualNames = e
    
    105
    +      | overloaded = e
    
    106
    +      | otherwise =
    
    107
    +          -- explicitly add ":: String" if not overloaded
    
    108
    +          noLocA . ExprWithTySig noExtField e $
    
    109
    +            HsWC
    
    110
    +              { hswc_ext = []
    
    111
    +              , hswc_body =
    
    112
    +                  noLocA
    
    113
    +                    HsSig
    
    114
    +                      { sig_ext   = noExtField
    
    115
    +                      , sig_bndrs = HsOuterImplicit []
    
    116
    +                      , sig_body  = nlHsTyVar NotPromoted stringTyConName
    
    117
    +                      }
    
    118
    +              }
    
    119
    +
    
    120
    +    mkStringLit s = do
    
    96 121
           let src = SourceText $ fsLit $ "\"" ++ unpackFS s ++ "\""
    
    97
    -      if overloaded
    
    98
    -        then do
    
    99
    -          (expr, fvs) <- rnOverLit noExtField $ OverLit noExtField (HsIsString src s)
    
    100
    -          pure (noLocA expr, fvs)
    
    101
    -        else
    
    102
    -          pure (nlHsLit $ HsString src strType s, emptyFVs)
    122
    +      let lit = nlHsLit $ HsString src strType s
    
    123
    +      if
    
    124
    +        | Just qualNames <- mQualNames -> do
    
    125
    +            pure (nlHsApp (nlHsVar $ qualFromString qualNames) lit, emptyFVs)
    
    126
    +        | overloaded -> do
    
    127
    +            (expr, fvs) <- rnOverLit noExtField $ OverLit noExtField (HsIsString src s)
    
    128
    +            pure (noLocA expr, fvs)
    
    129
    +        | otherwise -> do
    
    130
    +            pure (lit, emptyFVs)
    
    131
    +
    
    132
    +data QualifiedLiteralStringsNames = QualifiedLiteralStringsNames
    
    133
    +  { qualFromString :: Name
    
    134
    +  , qualInterpolate :: Name
    
    135
    +  , qualFromParts :: Name
    
    136
    +  }
    
    137
    +
    
    138
    +lookupQualifiedLiteralStringsNames ::
    
    139
    +  ModuleName -> RnM QualifiedLiteralStringsNames
    
    140
    +lookupQualifiedLiteralStringsNames modName = do
    
    141
    +  qualFromString <- lookup "fromString"
    
    142
    +  qualInterpolate <- lookup "interpolate"
    
    143
    +  qualFromParts <- lookup "fromParts"
    
    144
    +  pure QualifiedLiteralStringsNames{..}
    
    145
    +  where
    
    146
    +    lookup = lookupOccRn WL_None . mkRdrQual modName . mkVarOcc

  • compiler/GHC/Tc/Types/Origin.hs
    ... ... @@ -727,7 +727,7 @@ exprCtOrigin (ExplicitList {}) = ListOrigin
    727 727
     exprCtOrigin (HsIPVar _ ip)       = IPOccOrigin ip
    
    728 728
     exprCtOrigin (HsOverLit _ lit)    = LiteralOrigin lit
    
    729 729
     exprCtOrigin (HsLit {})           = Shouldn'tHappenOrigin "concrete literal"
    
    730
    -exprCtOrigin (HsInterString _ _ _) = InterStringOrigin
    
    730
    +exprCtOrigin (HsInterString _ _ _ _) = InterStringOrigin
    
    731 731
     exprCtOrigin (HsLam _ _ ms)       = matchesCtOrigin ms
    
    732 732
     exprCtOrigin (HsApp _ e1 _)       = lexprCtOrigin e1
    
    733 733
     exprCtOrigin (HsAppType _ e1 _)   = lexprCtOrigin e1
    

  • compiler/GHC/Tc/Zonk/Type.hs
    ... ... @@ -955,7 +955,7 @@ zonkExpr (HsOverLit x lit)
    955 955
       = do  { lit' <- zonkOverLit lit
    
    956 956
             ; return (HsOverLit x lit') }
    
    957 957
     
    
    958
    -zonkExpr (HsInterString x _ _) = dataConCantHappen x
    
    958
    +zonkExpr (HsInterString x _ _ _) = dataConCantHappen x
    
    959 959
     
    
    960 960
     zonkExpr (HsLam x lam_variant matches)
    
    961 961
       = do new_matches <- zonkMatchGroup zonkLExpr matches
    

  • compiler/GHC/ThToHs.hs
    ... ... @@ -1226,11 +1226,12 @@ cvtl e = wrapLA (cvt e)
    1226 1226
              ; let tele = setTelescopeBndrsNameSpace varName $
    
    1227 1227
                           mkHsForAllVisTele noAnn tvs'
    
    1228 1228
              ; return $ HsForAll noExtField tele body' }
    
    1229
    -    cvt (InterStringE parts) = do
    
    1229
    +    cvt (InterStringE mQualMod parts) = do
    
    1230
    +      let mQualMod' = mk_mod <$> mQualMod
    
    1230 1231
           parts' <- forM parts $ \case
    
    1231 1232
             InterStringRaw s -> pure $ HsInterStringRaw (SourceText $ fsLit s) (fsLit s)
    
    1232 1233
             InterStringExp e -> HsInterStringExpr noExtField <$> cvtl e
    
    1233
    -      return $ HsInterString noExtField HsStringTypeSingle parts'
    
    1234
    +      return $ HsInterString noExtField mQualMod' HsStringTypeSingle parts'
    
    1234 1235
     
    
    1235 1236
     {- | #16895 Ensure an infix expression's operator is a variable/constructor.
    
    1236 1237
     Consider this example:
    

  • compiler/Language/Haskell/Syntax/Expr.hs
    ... ... @@ -349,6 +349,7 @@ data HsExpr p
    349 349
       | -- | See Note [Parsing interpolated strings]
    
    350 350
         HsInterString
    
    351 351
           (XInterString p)
    
    352
    +      (Maybe ModuleName) -- ^ Module, if using QualifiedLiterals
    
    352 353
           HsStringType
    
    353 354
           [HsInterStringPart p]
    
    354 355
     
    

  • libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs
    ... ... @@ -250,9 +250,13 @@ pprExp i (ForallE tvars body) =
    250 250
     pprExp i (ConstrainedE ctx body) =
    
    251 251
       parensIf (i >= funPrec) $ sep [pprCtxWith pprExp ctx, pprExp qualPrec body]
    
    252 252
     
    
    253
    -pprExp _ (InterStringE parts) =
    
    254
    -  text "s\""<> hcat (map pprInterStringPart parts) <> text "\""
    
    253
    +pprExp _ (InterStringE mQualMod parts) =
    
    254
    +  prefix <> char '"' <> hcat (map pprInterStringPart parts) <> char '"'
    
    255 255
       where
    
    256
    +    prefix =
    
    257
    +      case mQualMod of
    
    258
    +        Nothing -> char 's'
    
    259
    +        Just qualMod -> text (modString qualMod) <> char '.'
    
    256 260
         pprInterStringPart = \case
    
    257 261
           InterStringRaw s -> text s
    
    258 262
           InterStringExp e -> text "${" <> pprExp noPrec e <> text "}"
    

  • libraries/ghc-experimental/src/Data/String/Interpolate/Experimental.hs
    ... ... @@ -15,11 +15,7 @@ See the proposal for motivation and explanations:
    15 15
     https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0570-string-interpolation.rst
    
    16 16
     -}
    
    17 17
     module Data.String.Interpolate.Experimental (
    
    18
    -  Buildable (..),
    
    19 18
       Interpolate (..),
    
    20
    -
    
    21
    -  -- * Built-in builders
    
    22
    -  StringBuilder (..),
    
    23 19
     ) where
    
    24 20
     
    
    25 21
     import GHC.Internal.Data.String.Interpolate

  • libraries/ghc-internal/src/GHC/Internal/Data/String/Interpolate.hs
    ... ... @@ -20,62 +20,66 @@
    20 20
     -----------------------------------------------------------------------------
    
    21 21
     
    
    22 22
     module GHC.Internal.Data.String.Interpolate (
    
    23
    -  Buildable (..),
    
    24 23
       Interpolate (..),
    
    25
    -
    
    26
    -  -- * Built-in builders
    
    27
    -  StringBuilder (..),
    
    28 24
     ) where
    
    29 25
     
    
    30 26
     import GHC.Internal.Base
    
    31
    -import GHC.Internal.Data.Monoid (Endo (..))
    
    32
    -import GHC.Internal.Show (Show, shows)
    
    33
    -
    
    34
    --- | @Buildable s@ allows @s@ to be built from an interpolated string.
    
    35
    ---
    
    36
    --- Laws:
    
    37
    ---   * @fromBuilder . toBuilder === id@
    
    38
    ---   * @toBuilder . fromBuilder === id@
    
    39
    -class Monoid (Builder s) => Buildable s where
    
    40
    -  type Builder s = r | r -> s
    
    41
    -  toBuilder :: s -> Builder s
    
    42
    -  fromBuilder :: Builder s -> s
    
    27
    +import GHC.Internal.Data.Either (Either (..))
    
    28
    +import GHC.Internal.Data.List (intercalate)
    
    29
    +import GHC.Internal.Show (show)
    
    43 30
     
    
    44 31
     -- | @Interpolate a s@ allows a value of type @a@ to be interpolated
    
    45 32
     -- into a string interpolation of type @s@.
    
    46
    ---
    
    47
    --- Laws:
    
    48
    ---   * @interpolate \@s \@s = toBuilder@
    
    49
    ---   * @interpolate \@(Builder s) \@s = id@
    
    50
    -class Buildable s => Interpolate a s where
    
    51
    -  interpolate :: a -> Builder s
    
    52
    -
    
    53
    -newtype StringBuilder = StringBuilder (Endo String)
    
    54
    -  deriving newtype (Semigroup, Monoid)
    
    55
    -
    
    56
    -instance Buildable String where
    
    57
    -  type Builder String = StringBuilder
    
    58
    -  toBuilder = toStringBuilder
    
    59
    -  fromBuilder = fromStringBuilder
    
    60
    -
    
    61
    -{-# RULES
    
    62
    -"fromStringBuilder/toStringBuilder" forall s. fromStringBuilder (toStringBuilder s) = s
    
    63
    -"toStringBuilder/fromStringBuilder" forall s. toStringBuilder (fromStringBuilder s) = s
    
    64
    -  #-}
    
    65
    -
    
    66
    -toStringBuilder :: String -> StringBuilder
    
    67
    -toStringBuilder s = StringBuilder (Endo (s ++))
    
    68
    -{-# NOINLINE [2] toStringBuilder #-}
    
    69
    -
    
    70
    -fromStringBuilder :: StringBuilder -> String
    
    71
    -fromStringBuilder (StringBuilder (Endo f)) = f ""
    
    72
    -{-# NOINLINE [2] fromStringBuilder #-}
    
    33
    +class Interpolate a s where
    
    34
    +  interpolate :: a -> s
    
    73 35
     
    
    74 36
     instance Interpolate String String where
    
    75
    -  interpolate = toBuilder
    
    76
    -instance Interpolate StringBuilder String where
    
    77 37
       interpolate = id
    
    78 38
     instance Interpolate Char String where
    
    79 39
       interpolate = interpolate . (:[])
    
    80
    -instance {-# OVERLAPPABLE #-} Show a => Interpolate a String where
    
    81
    -  interpolate = StringBuilder . Endo . shows
    40
    +
    
    41
    +instance Interpolate Int String where
    
    42
    +  interpolate = show
    
    43
    +instance Interpolate Double String where
    
    44
    +  interpolate = show
    
    45
    +instance Interpolate Bool String where
    
    46
    +  interpolate = show
    
    47
    +
    
    48
    +instance {-# OVERLAPPABLE #-}
    
    49
    +  ( Interpolate a String
    
    50
    +  ) => Interpolate [a] String where
    
    51
    +  interpolate as = "[" ++ (intercalate ", " . map interpolate) as ++ "]"
    
    52
    +instance
    
    53
    +  ( Interpolate a String
    
    54
    +  , Interpolate b String
    
    55
    +  ) => Interpolate (Either a b) String where
    
    56
    +  interpolate (Left a) = "Left " ++ interpolate a
    
    57
    +  interpolate (Right b) = "Right " ++ interpolate b
    
    58
    +
    
    59
    +instance
    
    60
    +  ( Interpolate a String
    
    61
    +  , Interpolate b String
    
    62
    +  ) => Interpolate (a, b) String where
    
    63
    +  interpolate (a, b) =
    
    64
    +    mconcat
    
    65
    +      [ "("
    
    66
    +      , interpolate a
    
    67
    +      , ", "
    
    68
    +      , interpolate b
    
    69
    +      , ")"
    
    70
    +      ]
    
    71
    +instance
    
    72
    +  ( Interpolate a String
    
    73
    +  , Interpolate b String
    
    74
    +  , Interpolate c String
    
    75
    +  ) => Interpolate (a, b, c) String where
    
    76
    +  interpolate (a, b, c) =
    
    77
    +    mconcat
    
    78
    +      [ "("
    
    79
    +      , interpolate a
    
    80
    +      , ", "
    
    81
    +      , interpolate b
    
    82
    +      , ", "
    
    83
    +      , interpolate c
    
    84
    +      , ")"
    
    85
    +      ]

  • libraries/ghc-internal/src/GHC/Internal/LanguageExtensions.hs
    ... ... @@ -166,6 +166,7 @@ data Extension
    166 166
        | ListTuplePuns
    
    167 167
        | MultilineStrings
    
    168 168
        | StringInterpolation
    
    169
    +   | QualifiedLiterals
    
    169 170
        | ExplicitLevelImports
    
    170 171
        | ImplicitStagePersistence
    
    171 172
        deriving (Eq, Enum, Show, Generic, Bounded)
    

  • libraries/ghc-internal/src/GHC/Internal/TH/Lib.hs
    ... ... @@ -450,8 +450,8 @@ forallVisE tvars body = ForallVisE <$> sequenceA tvars <*> body
    450 450
     constrainedE :: Quote m => [m Exp] -> m Exp -> m Exp
    
    451 451
     constrainedE ctx body = ConstrainedE <$> sequenceA ctx <*> body
    
    452 452
     
    
    453
    -interStringE :: Quote m => [m InterStringPart] -> m Exp
    
    454
    -interStringE parts = InterStringE <$> sequenceA parts
    
    453
    +interStringE :: Quote m => Maybe ModName -> [m InterStringPart] -> m Exp
    
    454
    +interStringE mQualMod parts = InterStringE mQualMod <$> sequenceA parts
    
    455 455
     
    
    456 456
     -------------------------------------------------------------------------------
    
    457 457
     -- *   Dec
    

  • libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs
    ... ... @@ -1903,7 +1903,9 @@ data Exp
    1903 1903
       | ForallE [TyVarBndr Specificity] Exp -- ^ @forall \<vars\>. \<expr\>@
    
    1904 1904
       | ForallVisE [TyVarBndr ()] Exp      -- ^ @forall \<vars\> -> \<expr\>@
    
    1905 1905
       | ConstrainedE [Exp] Exp             -- ^ @\<ctxt\> => \<expr\>@
    
    1906
    -  | InterStringE [InterStringPart]     -- ^ @{ s"Name: ${personName}" }@
    
    1906
    +  | InterStringE                       -- ^ @{ s"Name: ${personName}" }@ or @{ Mod."A ${x}" }@
    
    1907
    +      (Maybe ModName)
    
    1908
    +      [InterStringPart]
    
    1907 1909
       deriving( Show, Eq, Ord, Data, Generic )
    
    1908 1910
     
    
    1909 1911
     -- | A (field name, expression) pair. See 'RecConE' and 'RecUpdE'.
    

  • testsuite/tests/driver/T4437.hs
    ... ... @@ -36,7 +36,7 @@ check title expected got
    36 36
     
    
    37 37
     -- See Note [Adding a language extension] in compiler/GHC/Driver/Session.hs.
    
    38 38
     expectedGhcOnlyExtensions :: [String]
    
    39
    -expectedGhcOnlyExtensions = [ "StringInterpolation" ]
    
    39
    +expectedGhcOnlyExtensions = [ "StringInterpolation", "QualifiedLiterals" ]
    
    40 40
     
    
    41 41
     expectedCabalOnlyExtensions :: [String]
    
    42 42
     expectedCabalOnlyExtensions = ["Generics",
    

  • testsuite/tests/parser/should_run/StringInterpolationOverloaded.hs
    1
    -{-# LANGUAGE DerivingStrategies #-}
    
    2
    -{-# LANGUAGE GeneralisedNewtypeDeriving #-}
    
    3 1
     {-# LANGUAGE MultilineStrings #-}
    
    4 2
     {-# LANGUAGE OverloadedStrings #-}
    
    5 3
     {-# LANGUAGE RecordWildCards #-}
    
    ... ... @@ -77,17 +75,9 @@ data SqlValue
    77 75
       | SqlInt Int
    
    78 76
       deriving (Show)
    
    79 77
     
    
    80
    -newtype SqlQueryBuilder = SqlQueryBuilder (Endo SqlQuery)
    
    81
    -  deriving newtype (Semigroup, Monoid)
    
    82
    -
    
    83
    -instance Buildable SqlQuery where
    
    84
    -  type Builder SqlQuery = SqlQueryBuilder
    
    85
    -  toBuilder q = SqlQueryBuilder (Endo (q <>))
    
    86
    -  fromBuilder (SqlQueryBuilder (Endo f)) = f mempty
    
    87
    -
    
    88 78
     instance Interpolate SqlQuery SqlQuery where
    
    89
    -  interpolate = toBuilder
    
    79
    +  interpolate = id
    
    90 80
     instance Interpolate String SqlQuery where
    
    91
    -  interpolate s = toBuilder SqlQuery{sqlText = "?", sqlValues = [SqlString s]}
    
    81
    +  interpolate s = SqlQuery{sqlText = "?", sqlValues = [SqlString s]}
    
    92 82
     instance Interpolate Int SqlQuery where
    
    93
    -  interpolate x = toBuilder SqlQuery{sqlText = "?", sqlValues = [SqlInt x]}
    83
    +  interpolate x = SqlQuery{sqlText = "?", sqlValues = [SqlInt x]}

  • testsuite/tests/parser/should_run/StringInterpolationQualified.hs
    1
    +{-# LANGUAGE MultilineStrings #-}
    
    2
    +{-# LANGUAGE QualifiedLiterals #-}
    
    3
    +{-# LANGUAGE RecordWildCards #-}
    
    4
    +
    
    5
    +import qualified StringInterpolationQualified_SQL as SQL
    
    6
    +
    
    7
    +main :: IO ()
    
    8
    +main = mapM_ runTest allTests
    
    9
    +
    
    10
    +data TestCase =
    
    11
    +  forall a. Show a =>
    
    12
    +  TestCase
    
    13
    +    { label      :: String
    
    14
    +    , expression :: a
    
    15
    +    }
    
    16
    +
    
    17
    +runTest :: TestCase -> IO ()
    
    18
    +runTest TestCase{..} = do
    
    19
    +  putStrLn $ "****************************************"
    
    20
    +  putStrLn $ "Input:"
    
    21
    +  putStr   $ unlines . map ("    " ++) . lines $ label
    
    22
    +  putStrLn $ "====>"
    
    23
    +  putStrLn $ "    " ++ show expression
    
    24
    +
    
    25
    +allTests :: [TestCase]
    
    26
    +allTests =
    
    27
    +  [ TestCase -- custom interpolation implementations
    
    28
    +      { label =
    
    29
    +          """
    
    30
    +          let
    
    31
    +            name = "'Robert'; DROP TABLE Students;--"
    
    32
    +            age = 10 :: Int
    
    33
    +          in
    
    34
    +            SQL."SELECT * FROM tab WHERE name ILIKE ${name} and age = ${age}"
    
    35
    +          """
    
    36
    +      , expression =
    
    37
    +          let
    
    38
    +            name = "'Robert'; DROP TABLE Students;--"
    
    39
    +            age = 10 :: Int
    
    40
    +          in
    
    41
    +            SQL."SELECT * FROM tab WHERE name ILIKE ${name} and age = ${age}"
    
    42
    +      }
    
    43
    +  -- TODO(bchinn): qualified interpolated multiline string
    
    44
    +  ]

  • testsuite/tests/parser/should_run/StringInterpolationQualified.stdout
    1
    +****************************************
    
    2
    +Input:
    
    3
    +    let
    
    4
    +      name = "'Robert'; DROP TABLE Students;--"
    
    5
    +      age = 10 :: Int
    
    6
    +    in
    
    7
    +      SQL."SELECT * FROM tab WHERE name ILIKE ${name} and age = ${age}"
    
    8
    +====>
    
    9
    +    SqlQuery {sqlText = "SELECT * FROM tab WHERE name ILIKE ? and age = ?", sqlValues = [SqlString "'Robert'; DROP TABLE Students;--",SqlInt 10]}

  • testsuite/tests/parser/should_run/StringInterpolationQualified_SQL.hs
    1
    +module StringInterpolationQualified_SQL where
    
    2
    +
    
    3
    +import qualified Data.String
    
    4
    +
    
    5
    +data SqlQuery = SqlQuery
    
    6
    +  { sqlText :: String
    
    7
    +  , sqlValues :: [SqlValue]
    
    8
    +  }
    
    9
    +  deriving (Show)
    
    10
    +
    
    11
    +instance Data.String.IsString SqlQuery where
    
    12
    +  fromString s = SqlQuery{sqlText = s, sqlValues = []}
    
    13
    +instance Semigroup SqlQuery where
    
    14
    +  q1 <> q2 =
    
    15
    +    SqlQuery
    
    16
    +      { sqlText = sqlText q1 <> sqlText q2
    
    17
    +      , sqlValues = sqlValues q1 <> sqlValues q2
    
    18
    +      }
    
    19
    +instance Monoid SqlQuery where
    
    20
    +  mempty =
    
    21
    +    SqlQuery
    
    22
    +      { sqlText = ""
    
    23
    +      , sqlValues = []
    
    24
    +      }
    
    25
    +
    
    26
    +data SqlValue
    
    27
    +  = SqlString String
    
    28
    +  | SqlInt Int
    
    29
    +  deriving (Show)
    
    30
    +
    
    31
    +class ToSqlValue a where
    
    32
    +  toSqlValue :: a -> SqlValue
    
    33
    +instance ToSqlValue String where
    
    34
    +  toSqlValue = SqlString
    
    35
    +instance ToSqlValue Int where
    
    36
    +  toSqlValue = SqlInt
    
    37
    +
    
    38
    +{----- QualifiedLiterals -----}
    
    39
    +
    
    40
    +fromString :: String -> SqlQuery
    
    41
    +fromString = Data.String.fromString
    
    42
    +
    
    43
    +interpolate :: ToSqlValue a => a -> SqlQuery
    
    44
    +interpolate v = SqlQuery{sqlText = "?", sqlValues = [toSqlValue v]}
    
    45
    +
    
    46
    +fromParts :: [SqlQuery] -> SqlQuery
    
    47
    +fromParts = mconcat

  • testsuite/tests/parser/should_run/all.T
    ... ... @@ -38,3 +38,4 @@ test('T25784', normal, compile_and_run, [''])
    38 38
     # String interpolation
    
    39 39
     test('StringInterpolation', normal, compile_and_run, [''])
    
    40 40
     test('StringInterpolationOverloaded', normal, compile_and_run, [''])
    
    41
    +test('StringInterpolationQualified', [extra_files(['StringInterpolationQualified_SQL.hs'])], multimod_compile_and_run, ['StringInterpolationQualified', ''])