-- LocalRdrEnv
LocalRdrEnv, emptyLocalRdrEnv, extendLocalRdrEnv,
- lookupLocalRdrEnv, elemLocalRdrEnv,
+ lookupLocalRdrEnv, lookupLocalRdrOcc, elemLocalRdrEnv,
-- GlobalRdrEnv
GlobalRdrEnv, emptyGlobalRdrEnv, mkGlobalRdrEnv, plusGlobalRdrEnv,
lookupLocalRdrEnv env (Unqual occ) = lookupOccEnv env occ
lookupLocalRdrEnv env other = Nothing
+lookupLocalRdrOcc :: LocalRdrEnv -> OccName -> Maybe Name
+lookupLocalRdrOcc env occ = lookupOccEnv env occ
+
elemLocalRdrEnv :: RdrName -> LocalRdrEnv -> Bool
elemLocalRdrEnv rdr_name env
| isUnqual rdr_name = rdrNameOcc rdr_name `elemOccEnv` env
\begin{code}
lookupGlobalRdrEnv :: GlobalRdrEnv -> OccName -> [GlobalRdrElt]
-lookupGlobalRdrEnv env rdr_name = case lookupOccEnv env rdr_name of
+lookupGlobalRdrEnv env occ_name = case lookupOccEnv env occ_name of
Nothing -> []
Just gres -> gres
| UnhelpfulSpan FastString -- Just a general indication
-- also used to indicate an empty span
+#ifdef DEBUG
+ deriving (Eq, Show) -- Show is used by Lexer.x, becuase we
+ -- derive Show for Token
+#else
deriving Eq
+#endif
-- We want to order SrcSpans first by the start point, then by the end point.
instance Ord SrcSpan where
module DsMeta( dsBracket,
templateHaskellNames, qTyConName, nameTyConName,
- liftName, expQTyConName, decQTyConName, typeQTyConName,
- decTyConName, typeTyConName, mkNameG_dName, mkNameG_vName, mkNameG_tcName
+ liftName, expQTyConName, patQTyConName, decQTyConName, typeQTyConName,
+ decTyConName, typeTyConName, mkNameG_dName, mkNameG_vName, mkNameG_tcName,
+ quoteExpName, quotePatName
) where
#include "HsVersions.h"
decQTyConName, conQTyConName, strictTypeQTyConName,
varStrictTypeQTyConName, typeQTyConName, expTyConName, decTyConName,
typeTyConName, matchTyConName, clauseTyConName, patQTyConName,
- fieldPatQTyConName, fieldExpQTyConName, funDepTyConName]
+ fieldPatQTyConName, fieldExpQTyConName, funDepTyConName,
+
+ -- Quasiquoting
+ quoteExpName, quotePatName]
thSyn :: Module
thSyn = mkTHModule FSLIT("Language.Haskell.TH.Syntax")
thLib = mkTHModule FSLIT("Language.Haskell.TH.Lib")
+qqLib = mkTHModule FSLIT("Language.Haskell.TH.Quote")
mkTHModule m = mkModule thPackageId (mkModuleNameFS m)
libTc = mk_known_key_name OccName.tcName thLib
thFun = mk_known_key_name OccName.varName thSyn
thTc = mk_known_key_name OccName.tcName thSyn
+qqFun = mk_known_key_name OccName.varName qqLib
-------------------- TH.Syntax -----------------------
qTyConName = thTc FSLIT("Q") qTyConKey
patQTyConName = libTc FSLIT("PatQ") patQTyConKey
fieldPatQTyConName = libTc FSLIT("FieldPatQ") fieldPatQTyConKey
+-- quasiquoting
+quoteExpName = qqFun FSLIT("quoteExp") quoteExpKey
+quotePatName = qqFun FSLIT("quotePat") quotePatKey
+
-- TyConUniques available: 100-129
-- Check in PrelNames if you want to change this
-- data FunDep = ...
funDepIdKey = mkPreludeMiscIdUnique 320
+-- quasiquoting
+quoteExpKey = mkPreludeMiscIdUnique 321
+quotePatKey = mkPreludeMiscIdUnique 322
+
-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
-- for details
-module Convert( convertToHsExpr, convertToHsDecls,
+module Convert( convertToHsExpr, convertToPat, convertToHsDecls,
convertToHsType, thRdrName ) where
#include "HsVersions.h"
<+> text (show e)))
Right res -> Right res
+convertToPat :: SrcSpan -> TH.Pat -> Either Message (LPat RdrName)
+convertToPat loc e
+ = case initCvt loc (cvtPat e) of
+ Left msg -> Left (msg $$ (ptext SLIT("When converting TH pattern")
+ <+> text (show e)))
+ Right res -> Right res
+
convertToHsType :: SrcSpan -> TH.Type -> Either Message (LHsType RdrName)
convertToHsType loc t = initCvt loc (cvtType t)
| HsSpliceE (HsSplice id)
+ | HsQuasiQuoteE (HsQuasiQuote id)
+ -- See Note [Quasi-quote overview] in TcSplice
+
-----------------------------------------------------------
-- Arrow notation extension
ppr_expr (HsBracket b) = pprHsBracket b
ppr_expr (HsBracketOut e []) = ppr e
ppr_expr (HsBracketOut e ps) = ppr e $$ ptext SLIT("pending") <+> ppr ps
+ppr_expr (HsQuasiQuoteE (HsQuasiQuote name quoter _ quote))
+ = char '$' <> brackets (ppr name) <>
+ ptext SLIT("[:") <> ppr quoter <> ptext SLIT("|") <>
+ ppr quote <> ptext SLIT("|]")
ppr_expr (HsProc pat (L _ (HsCmdTop cmd _ _ _)))
= hsep [ptext SLIT("proc"), ppr pat, ptext SLIT("->"), ppr cmd]
module HsPat where
+data HsQuasiQuote i = HsQuasiQuote i i SrcSpan FastString
+
data Pat i
type LPat i = SrcLoc.Located (Pat i)
HsConPatDetails, hsConPatArgs,
HsRecFields(..), HsRecField(..), hsRecFields,
+ HsQuasiQuote(..),
+
mkPrefixConPat, mkCharLitPat, mkNilPat, mkCoPat, mkCoPatCoI,
isBangHsBind,
import Outputable
import Type
import SrcLoc
+import FastString
\end{code}
-- (= the argument type of the view function)
-- for hsPatType.
+ ------------ Quasiquoted patterns ---------------
+ -- See Note [Quasi-quote overview] in TcSplice
+ | QuasiQuotePat (HsQuasiQuote id)
+
------------ Literal and n+k patterns ---------------
| LitPat HsLit -- Used for *non-overloaded* literal patterns:
-- Int#, Char#, Int, Char, String, etc.
hsRecFields rbinds = map (unLoc . hsRecFieldId) (rec_flds rbinds)
\end{code}
+\begin{code}
+data HsQuasiQuote id = HsQuasiQuote
+ id
+ id
+ SrcSpan
+ FastString
+\end{code}
+
%************************************************************************
%* *
pprPat (NPat l Nothing _) = ppr l
pprPat (NPat l (Just _) _) = char '-' <> ppr l
pprPat (NPlusKPat n k _ _) = hcat [ppr n, char '+', ppr k]
+pprPat (QuasiQuotePat (HsQuasiQuote name quoter _ quote))
+ = char '$' <> brackets (ppr name) <>
+ ptext SLIT("[:") <> ppr quoter <> ptext SLIT("|") <>
+ ppr quote <> ptext SLIT("|]")
pprPat (TypePat ty) = ptext SLIT("{|") <> ppr ty <> ptext SLIT("|}")
pprPat (CoPat co pat _) = parens (pprHsWrapper (ppr pat) co)
pprPat (SigPatIn pat ty) = ppr pat <+> dcolon <+> ppr ty
\begin{code}
module HsPat where
-import SrcLoc( Located )
+import SrcLoc( Located, SrcSpan )
+import FastString ( FastString )
+
+data HsQuasiQuote i = HsQuasiQuote i i SrcSpan FastString
data Pat i
type LPat i = Located (Pat i)
-- A name (uniquified later) to
-- identify the splice
+mkHsQuasiQuote quoter span quote = HsQuasiQuote unqualQuasiQuote quoter span quote
+
+unqualQuasiQuote = mkRdrUnqual (mkVarOccFS FSLIT("quasiquote"))
+ -- A name (uniquified later) to
+ -- identify the quasi-quote
+
mkHsString s = HsString (mkFastString s)
-------------
go (SigPatIn pat _) = collectl pat bndrs
go (SigPatOut pat _) = collectl pat bndrs
+ go (QuasiQuotePat _) = bndrs
go (TypePat ty) = bndrs
go (CoPat _ pat ty) = collectl (noLoc pat) bndrs
\end{code}
| Opt_PArr -- Syntactic support for parallel arrays
| Opt_Arrows -- Arrow-notation syntax
| Opt_TemplateHaskell
+ | Opt_QuasiQuotes
| Opt_ImplicitParams
| Opt_Generics
| Opt_ImplicitPrelude
( "Arrows", Opt_Arrows ),
( "PArr", Opt_PArr ),
( "TemplateHaskell", Opt_TemplateHaskell ),
+ ( "QuasiQuotes", Opt_QuasiQuotes ),
( "Generics", Opt_Generics ),
-- On by default:
( "ImplicitPrelude", Opt_ImplicitPrelude ),
"|]" / { ifExtension thEnabled } { token ITcloseQuote }
\$ @varid / { ifExtension thEnabled } { skip_one_varid ITidEscape }
"$(" / { ifExtension thEnabled } { token ITparenEscape }
+
+ "[$" @varid "|" / { ifExtension qqEnabled }
+ { lex_quasiquote_tok }
}
<0> {
| ITparenEscape -- $(
| ITvarQuote -- '
| ITtyQuote -- ''
+ | ITquasiQuote (FastString,FastString,SrcSpan) -- [:...|...|]
-- Arrow notation extension
| ITproc
Just (c,i) -> do setInput i; return c
-- -----------------------------------------------------------------------------
+-- QuasiQuote
+
+lex_quasiquote_tok :: Action
+lex_quasiquote_tok span buf len = do
+ let quoter = reverse $ takeWhile (/= '$')
+ $ reverse $ lexemeToString buf (len - 1)
+ quoteStart <- getSrcLoc
+ quote <- lex_quasiquote ""
+ end <- getSrcLoc
+ return (L (mkSrcSpan (srcSpanStart span) end)
+ (ITquasiQuote (mkFastString quoter,
+ mkFastString (reverse quote),
+ mkSrcSpan quoteStart end)))
+
+lex_quasiquote :: String -> P String
+lex_quasiquote s = do
+ i <- getInput
+ case alexGetChar' i of
+ Nothing -> lit_error
+
+ Just ('\\',i)
+ | Just ('|',i) <- next -> do
+ setInput i; lex_quasiquote ('|' : s)
+ | Just (']',i) <- next -> do
+ setInput i; lex_quasiquote (']' : s)
+ where next = alexGetChar' i
+
+ Just ('|',i)
+ | Just (']',i) <- next -> do
+ setInput i; return s
+ where next = alexGetChar' i
+
+ Just (c, i) -> do
+ setInput i; lex_quasiquote (c : s)
+
+-- -----------------------------------------------------------------------------
-- Warnings
warn :: DynFlag -> SDoc -> Action
unboxedTuplesBit = 15 -- (# and #)
standaloneDerivingBit = 16 -- standalone instance deriving declarations
transformComprehensionsBit = 17
+qqBit = 18 -- enable quasiquoting
genericsEnabled, ffiEnabled, parrEnabled :: Int -> Bool
always _ = True
unboxedTuplesEnabled flags = testBit flags unboxedTuplesBit
standaloneDerivingEnabled flags = testBit flags standaloneDerivingBit
transformComprehensionsEnabled flags = testBit flags transformComprehensionsBit
+qqEnabled flags = testBit flags qqBit
-- PState for parsing options pragmas
--
.|. parrBit `setBitIf` dopt Opt_PArr flags
.|. arrowsBit `setBitIf` dopt Opt_Arrows flags
.|. thBit `setBitIf` dopt Opt_TemplateHaskell flags
+ .|. qqBit `setBitIf` dopt Opt_QuasiQuotes flags
.|. ipBit `setBitIf` dopt Opt_ImplicitParams flags
.|. explicitForallBit `setBitIf` dopt Opt_ScopedTypeVariables flags
.|. explicitForallBit `setBitIf` dopt Opt_PolymorphicComponents flags
'$(' { L _ ITparenEscape } -- $( exp )
TH_VAR_QUOTE { L _ ITvarQuote } -- 'x
TH_TY_QUOTE { L _ ITtyQuote } -- ''T
+TH_QUASIQUOTE { L _ (ITquasiQuote _) }
%monad { P } { >>= } { return }
%lexer { lexer } { L _ ITeof }
(getTH_ID_SPLICE $1)))) } -- $x
| '$(' exp ')' { LL $ HsSpliceE (mkHsSplice $2) } -- $( exp )
+ | TH_QUASIQUOTE { let { loc = getLoc $1
+ ; ITquasiQuote (quoter, quote, quoteSpan) = unLoc $1
+ ; quoterId = mkUnqual varName quoter
+ }
+ in sL loc $ HsQuasiQuoteE (mkHsQuasiQuote quoterId quoteSpan quote) }
| TH_VAR_QUOTE qvar { LL $ HsBracket (VarBr (unLoc $2)) }
| TH_VAR_QUOTE qcon { LL $ HsBracket (VarBr (unLoc $2)) }
| TH_TY_QUOTE tyvar { LL $ HsBracket (VarBr (unLoc $2)) }
RecordCon c _ (HsRecFields fs dd)
-> mapM checkPatField fs >>= \fs ->
return (ConPatIn c (RecCon (HsRecFields fs dd)))
+ HsQuasiQuoteE q -> return (QuasiQuotePat q)
-- Generics
HsType ty -> return (TypePat ty)
_ -> patFail loc
bindLocatedLocalsFV, bindLocalNames, bindLocalNamesFV,
bindLocalNamesFV_WithFixities,
bindLocatedLocalsRn,
- checkDupNames, checkShadowing
+ checkDupAndShadowedRdrNames
)
import DynFlags ( DynFlag(..) )
import HscTypes (FixItem(..))
-- Do error checking: we need to check for dups here because we
-- don't don't bind all of the variables from the ValBinds at once
-- with bindLocatedLocals any more.
- checkDupNames doc boundNames
- checkShadowing doc boundNames
+ checkDupAndShadowedRdrNames doc boundNames
-- (Note that we don't want to do this at the top level, since
-- sorting out duplicates and shadowing there happens elsewhere.
bindTyVarsRn, extendTyVarEnvFVRn,
bindLocalFixities,
- checkDupNames, checkShadowing, mapFvRn, mapFvRnCPS,
+ checkDupRdrNames, checkDupNames, checkShadowedNames,
+ checkDupAndShadowedRdrNames,
+ mapFvRn, mapFvRnCPS,
warnUnusedMatches, warnUnusedModules, warnUnusedImports,
warnUnusedTopBinds, warnUnusedLocalBinds,
dataTcOccs, unknownNameErr,
LHsTyVarBndr, LHsType,
Fixity, hsLTyVarLocNames, replaceTyVarName )
import RdrHsSyn ( extractHsTyRdrTyVars )
-import RdrName ( RdrName, isQual, isUnqual, isOrig_maybe,
- isQual_maybe,
- mkRdrUnqual, setRdrNameSpace, rdrNameOcc,
- pprGlobalRdrEnv, lookupGRE_RdrName,
- isExact_maybe, isSrcRdrName,
- Parent(..),
- GlobalRdrElt(..), GlobalRdrEnv, lookupGlobalRdrEnv,
- isLocalGRE, extendLocalRdrEnv, elemLocalRdrEnv, lookupLocalRdrEnv,
- Provenance(..), pprNameProvenance,
- importSpecLoc, importSpecModule
- )
+import RdrName
import HscTypes ( availNames, ModIface(..), FixItem(..), lookupFixity)
import TcEnv ( tcLookupDataCon )
import TcRnMonad
import Name ( Name, nameIsLocalOrFrom, mkInternalName, isWiredInName,
- nameSrcLoc, nameOccName, nameModule, isExternalName )
+ nameSrcLoc, nameSrcSpan, nameOccName, nameModule, isExternalName )
import NameSet
import NameEnv
import UniqFM
import DataCon ( dataConFieldLabels )
-import OccName ( tcName, isDataOcc, pprNonVarNameSpace, occNameSpace,
+import OccName ( OccName, tcName, isDataOcc, pprNonVarNameSpace, occNameSpace,
reportIfUnused, occNameFS )
import Module ( Module, ModuleName )
import PrelNames ( mkUnboundName, rOOT_MAIN, iNTERACTIVE, consDataConKey, hasKey )
getLookupOccRn :: RnM (Name -> Maybe Name)
getLookupOccRn
= getLocalRdrEnv `thenM` \ local_env ->
- return (lookupLocalRdrEnv local_env . mkRdrUnqual . nameOccName)
+ return (lookupLocalRdrOcc local_env . nameOccName)
lookupLocatedOccRn :: Located RdrName -> RnM (Located Name)
lookupLocatedOccRn = wrapLocM lookupOccRn
-- lookupRdrEnv doesn't even attempt to look up a qualified RdrName
mkInternalName uniq (rdrNameOcc rdr_name) loc
+---------------------
+checkDupAndShadowedRdrNames :: SDoc -> [Located RdrName] -> RnM ()
+checkDupAndShadowedRdrNames doc loc_rdr_names
+ = do { checkDupRdrNames doc loc_rdr_names
+ ; envs <- getRdrEnvs
+ ; checkShadowedNames doc envs
+ [(loc,rdrNameOcc rdr) | L loc rdr <- loc_rdr_names] }
+
+---------------------
bindLocatedLocalsRn :: SDoc -- Documentation string for error message
-> [Located RdrName]
-> ([Name] -> RnM a)
-> RnM a
bindLocatedLocalsRn doc_str rdr_names_w_loc enclosed_scope
- = -- Check for duplicate names
- checkDupNames doc_str rdr_names_w_loc `thenM_`
-
- -- Warn about shadowing
- checkShadowing doc_str rdr_names_w_loc `thenM_`
+ = checkDupAndShadowedRdrNames doc_str rdr_names_w_loc `thenM_`
-- Make fresh Names and extend the environment
newLocalsRn rdr_names_w_loc `thenM` \names ->
extendTyVarEnvFVRn tyvars thing_inside = bindLocalNamesFV tyvars thing_inside
-------------------------------------
+checkDupRdrNames :: SDoc
+ -> [Located RdrName]
+ -> RnM ()
+checkDupRdrNames doc_str rdr_names_w_loc
+ = -- Check for duplicated names in a binding group
+ mappM_ (dupNamesErr getLoc doc_str) dups
+ where
+ (_, dups) = removeDups (\n1 n2 -> unLoc n1 `compare` unLoc n2) rdr_names_w_loc
+
checkDupNames :: SDoc
- -> [Located RdrName]
+ -> [Name]
-> RnM ()
-checkDupNames doc_str rdr_names_w_loc
+checkDupNames doc_str names
= -- Check for duplicated names in a binding group
- mappM_ (dupNamesErr doc_str) dups
+ mappM_ (dupNamesErr nameSrcSpan doc_str) dups
where
- (_, dups) = removeDups (\n1 n2 -> unLoc n1 `compare` unLoc n2) rdr_names_w_loc
+ (_, dups) = removeDups (\n1 n2 -> nameOccName n1 `compare` nameOccName n2) names
-------------------------------------
-checkShadowing doc_str loc_rdr_names
- = traceRn (text "shadow" <+> ppr loc_rdr_names) `thenM_`
- getLocalRdrEnv `thenM` \ local_env ->
- getGlobalRdrEnv `thenM` \ global_env ->
- let
- check_shadow (L loc rdr_name)
- | Just n <- mb_local = complain [ptext SLIT("bound at") <+> ppr (nameSrcLoc n)]
+checkShadowedNames :: SDoc -> (GlobalRdrEnv, LocalRdrEnv) -> [(SrcSpan,OccName)] -> RnM ()
+checkShadowedNames doc_str (global_env,local_env) loc_rdr_names
+ = ifOptM Opt_WarnNameShadowing $
+ do { traceRn (text "shadow" <+> ppr loc_rdr_names)
+ ; mappM_ check_shadow loc_rdr_names }
+ where
+ check_shadow (loc, occ)
+ | Just n <- mb_local = complain [ptext SLIT("bound at") <+> ppr loc]
| not (null gres) = complain (map pprNameProvenance gres)
| otherwise = return ()
where
- complain pp_locs = addWarnAt loc (shadowedNameWarn doc_str rdr_name pp_locs)
- mb_local = lookupLocalRdrEnv local_env rdr_name
- gres = lookupGRE_RdrName rdr_name global_env
- in
- ifOptM Opt_WarnNameShadowing (mappM_ check_shadow loc_rdr_names)
+ complain pp_locs = addWarnAt loc (shadowedNameWarn doc_str occ pp_locs)
+ mb_local = lookupLocalRdrOcc local_env occ
+ gres = lookupGlobalRdrEnv global_env occ
\end{code}
msgs = [ptext SLIT(" or") <+> mk_ref np | np <- nps]
mk_ref gre = quotes (ppr (gre_name gre)) <> comma <+> pprNameProvenance gre
-shadowedNameWarn doc rdr_name shadowed_locs
- = sep [ptext SLIT("This binding for") <+> quotes (ppr rdr_name)
+shadowedNameWarn doc occ shadowed_locs
+ = sep [ptext SLIT("This binding for") <+> quotes (ppr occ)
<+> ptext SLIT("shadows the existing binding") <> plural shadowed_locs,
nest 2 (vcat shadowed_locs)]
$$ doc
= ptext SLIT("Illegal binding of built-in syntax:") <+> ppr (rdrNameOcc name)
-- The rdrNameOcc is because we don't want to print Prelude.(,)
-dupNamesErr :: SDoc -> [Located RdrName] -> RnM ()
-dupNamesErr descriptor located_names
+dupNamesErr :: Outputable n => (n -> SrcSpan) -> SDoc -> [n] -> RnM ()
+dupNamesErr get_loc descriptor names
= addErrAt big_loc $
- vcat [ptext SLIT("Conflicting definitions for") <+> quotes (ppr name1),
+ vcat [ptext SLIT("Conflicting definitions for") <+> quotes (ppr (head names)),
locations, descriptor]
where
- L _ name1 = head located_names
- locs = map getLoc located_names
+ locs = map get_loc names
big_loc = foldr1 combineSrcSpans locs
one_line = isOneLineSpan big_loc
locations | one_line = empty
#include "HsVersions.h"
+#ifdef GHCI
+import {-# SOURCE #-} TcSplice( runQuasiQuoteExpr )
+#endif /* GHCI */
+
import RnSource ( rnSrcDecls, rnSplice, checkTH )
import RnBinds ( rnLocalBindsAndThen, rnValBindsLHS, rnValBindsRHS,
rnMatchGroup, makeMiniFixityEnv)
import RnNames ( getLocalDeclBinders, extendRdrEnvRn )
import RnTypes ( rnHsTypeFVs,
mkOpFormRn, mkOpAppRn, mkNegAppRn, checkSectionPrec)
-import RnPat (rnOverLit, rnPatsAndThen_LocalRightwards, rnBindPat,
+import RnPat (rnQuasiQuote, rnOverLit, rnPatsAndThen_LocalRightwards, rnBindPat,
localRecNameMaker, rnLit,
rnHsRecFields_Con, rnHsRecFields_Update, checkTupSize)
import RdrName ( mkRdrUnqual )
= rnSplice splice `thenM` \ (splice', fvs) ->
returnM (HsSpliceE splice', fvs)
+#ifndef GHCI
+rnExpr e@(HsQuasiQuoteE _) = pprPanic "Cant do quasiquotation without GHCi" (ppr e)
+#else
+rnExpr e@(HsQuasiQuoteE qq)
+ = rnQuasiQuote qq `thenM` \ (qq', fvs_qq) ->
+ runQuasiQuoteExpr qq' `thenM` \ (L _ expr') ->
+ rnExpr expr' `thenM` \ (expr'', fvs_expr) ->
+ returnM (expr'', fvs_qq `plusFV` fvs_expr)
+#endif /* GHCI */
+
rnExpr section@(SectionL expr op)
= rnLExpr expr `thenM` \ (expr', fvs_expr) ->
rnLExpr op `thenM` \ (op', fvs_op) ->
-- First do error checking: we need to check for dups here because we
-- don't bind all of the variables from the Stmt at once
-- with bindLocatedLocals.
- checkDupNames doc boundNames
+ checkDupRdrNames doc boundNames
mappM (rn_rec_stmt_lhs fix_env) stmts `thenM` \ ls -> returnM (concat ls)
-- Literals
rnLit, rnOverLit,
+ -- Quasiquotation
+ rnQuasiQuote,
+
-- Pattern Error messages that are also used elsewhere
checkTupSize, patSigErr
) where
-- ENH: thin imports to only what is necessary for patterns
import {-# SOURCE #-} RnExpr( rnLExpr, rnStmts)
+#ifdef GHCI
+import {-# SOURCE #-} TcSplice( runQuasiQuotePat )
+#endif /* GHCI */
#include "HsVersions.h"
eqClassName, integralClassName, geName, eqName,
negateName, minusName, lengthPName, indexPName,
plusIntegerName, fromIntegerName, timesIntegerName,
- ratioDataConName, fromRationalName, fromStringName )
+ ratioDataConName, fromRationalName, fromStringName, mkUnboundName )
import Constants ( mAX_TUPLE_SIZE )
-import Name ( Name, nameOccName, nameIsLocalOrFrom, getOccName, nameSrcSpan )
+import Name ( Name, nameOccName, nameModule_maybe, getOccName, nameSrcSpan )
+import OccName ( occEnvElts )
import NameSet
import UniqFM
-import RdrName ( RdrName, extendLocalRdrEnv, lookupLocalRdrEnv, hideSomeUnquals, mkRdrUnqual, nameRdrName )
+import RdrName ( RdrName, GlobalRdrElt(..), Provenance(..),
+ extendLocalRdrEnv, lookupLocalRdrEnv, hideSomeUnquals,
+ mkRdrUnqual, nameRdrName, gre_name, globalRdrEnvElts, isLocalGRE )
import LoadIface ( loadInterfaceForName )
import UniqFM ( isNullUFM )
import UniqSet ( emptyUniqSet )
-> RnM (a, FreeVars)
rnPatsAndThen_LocalRightwards ctxt pats thing_inside
- = do { -- Check for duplicated and shadowed names
- -- Because we don't bind the vars all at once, we can't
- -- check incrementally for duplicates;
- -- Nor can we check incrementally for shadowing, else we'll
- -- complain *twice* about duplicates e.g. f (x,x) = ...
- let rdr_names_w_loc = collectLocatedPatsBinders pats
- ; checkDupNames doc_pat rdr_names_w_loc
- ; checkShadowing doc_pat rdr_names_w_loc
+ = do { envs_before <- getRdrEnvs
-- (0) bring into scope all of the type variables bound by the patterns
-- (1) rename the patterns, bringing into scope all of the term variables
-- (2) then do the thing inside.
; bindPatSigTyVarsFV (collectSigTysFromPats pats) $
- rnLPatsAndThen matchNameMaker pats $
- thing_inside }
+ rnLPatsAndThen matchNameMaker pats $ \ pats' ->
+ do { -- Check for duplicated and shadowed names
+ -- Because we don't bind the vars all at once, we can't
+ -- check incrementally for duplicates;
+ -- Nor can we check incrementally for shadowing, else we'll
+ -- complain *twice* about duplicates e.g. f (x,x) = ...
+ ; let names = collectPatsBinders pats'
+ ; checkDupNames doc_pat names
+ ; checkShadowedNames doc_pat envs_before
+ [(nameSrcSpan name, nameOccName name) | name <- names]
+ ; thing_inside pats' } }
where
doc_pat = ptext SLIT("In") <+> pprMatchContext ctxt
lcont (ViewPat expr' pat' ty)
; return (res, fvs_res `plusFV` fv_expr) }
+#ifndef GHCI
+ pat@(QuasiQuotePat _) -> pprPanic "Can't do QuasiQuotePat without GHCi" (ppr p)
+#else
+ QuasiQuotePat qq -> do
+ (qq', _) <- rnQuasiQuote qq
+ pat' <- runQuasiQuotePat qq'
+ rnLPatAndThen var pat' $ \ (L _ pat'') ->
+ lcont pat''
+#endif /* GHCI */
+
ConPatIn con stuff ->
-- rnConPatAndThen takes care of reconstructing the pattern
rnConPatAndThen var con stuff cont
returnM (HsIsString s from_string_name placeHolderType, fvs)
\end{code}
+%************************************************************************
+%* *
+\subsubsection{Quasiquotation}
+%* *
+%************************************************************************
+
+See Note [Quasi-quote overview] in TcSplice.
+
+\begin{code}
+rnQuasiQuote :: HsQuasiQuote RdrName -> RnM (HsQuasiQuote Name, FreeVars)
+rnQuasiQuote (HsQuasiQuote n quoter quoteSpan quote)
+ = do { loc <- getSrcSpanM
+ ; [n'] <- newLocalsRn [L loc n]
+ ; quoter' <- (lookupOccRn quoter)
+ -- If 'quoter' is not in scope, proceed no further
+ -- Otherwise lookupOcc adds an error messsage and returns
+ -- an "unubound name", which makes the subsequent attempt to
+ -- run the quote fail
+ ; return (HsQuasiQuote n' quoter' quoteSpan quote, unitFV quoter') }
+\end{code}
%************************************************************************
%* *
lookupOccRn, newLocalsRn,
bindLocatedLocalsFV, bindPatSigTyVarsFV,
bindTyVarsRn, extendTyVarEnvFVRn,
- bindLocalNames, checkDupNames, mapFvRn, lookupGreLocalRn,
+ bindLocalNames, checkDupRdrNames, mapFvRn, lookupGreLocalRn,
)
import RnNames (importsFromLocalDecls, extendRdrEnvRn)
import HscTypes (GenAvailInfo(..))
-- Used for both source and interface file decls
= rnHsSigType (text "an instance decl") inst_ty `thenM` \ inst_ty' ->
- -- Rename the associated types
- -- The typechecker (not the renamer) checks that all
- -- the declarations are for the right class
- let
- at_doc = text "In the associated types of an instance declaration"
- at_names = map (head . tyClDeclNames . unLoc) ats
- in
- checkDupNames at_doc at_names `thenM_`
- rnATInsts ats `thenM` \ (ats', at_fvs) ->
-
-- Rename the bindings
-- The typechecker (not the renamer) checks that all
-- the bindings are for the right class
meth_names = collectHsBindLocatedBinders mbinds
(inst_tyvars, _, cls,_) = splitHsInstDeclTy (unLoc inst_ty')
in
- checkDupNames meth_doc meth_names `thenM_`
+ checkDupRdrNames meth_doc meth_names `thenM_`
+ -- Check that the same method is not given twice in the
+ -- same instance decl instance C T where
+ -- f x = ...
+ -- g y = ...
+ -- f x = ...
+ -- We must use checkDupRdrNames because the Name of the
+ -- method is the Name of the class selector, whose SrcSpan
+ -- points to the class declaration
+
extendTyVarEnvForMethodBinds inst_tyvars (
-- (Slightly strangely) the forall-d tyvars scope over
-- the method bindings too
rnMethodBinds cls (\n->[]) -- No scoped tyvars
[] mbinds
) `thenM` \ (mbinds', meth_fvs) ->
+ -- Rename the associated types
+ -- The typechecker (not the renamer) checks that all
+ -- the declarations are for the right class
+ let
+ at_doc = text "In the associated types of an instance declaration"
+ at_names = map (head . tyClDeclNames . unLoc) ats
+ in
+ checkDupRdrNames at_doc at_names `thenM_`
+ -- See notes with checkDupRdrNames for methods, above
+
+ rnATInsts ats `thenM` \ (ats', at_fvs) ->
+
-- Rename the prags and signatures.
-- Note that the type variables are not in scope here,
-- so that instance Eq a => Eq (T a) where
; context' <- rnContext data_doc context
; typats' <- rnTyPats data_doc typatsMaybe
; (derivs', deriv_fvs) <- rn_derivs derivs
- ; checkDupNames data_doc con_names
; condecls' <- rnConDecls (unLoc tycon') condecls
+ -- No need to check for duplicate constructor decls
+ -- since that is done by RnNames.extendRdrEnvRn
; returnM (TyData {tcdND = new_or_data, tcdCtxt = context',
tcdLName = tycon', tcdTyVars = tyvars',
tcdTyPats = typats', tcdKindSig = Nothing,
-- do not scope over the constructor signatures
-- data T a where { T1 :: forall b. b-> b }
; (derivs', deriv_fvs) <- rn_derivs derivs
- ; checkDupNames data_doc con_names
; condecls' <- rnConDecls (unLoc tycon') condecls
+ -- No need to check for duplicate constructor decls
+ -- since that is done by RnNames.extendRdrEnvRn
; returnM (TyData {tcdND = new_or_data, tcdCtxt = noLoc [],
tcdLName = tycon', tcdTyVars = tyvars',
tcdTyPats = Nothing, tcdKindSig = sig,
; sigs' <- renameSigs okClsDclSig sigs
; return (tyvars', context', fds', ats', ats_fvs, sigs') }
- -- Check for duplicates among the associated types
- ; let at_rdr_names_w_locs = [tcdLName ty | L _ ty <- ats]
- ; checkDupNames at_doc at_rdr_names_w_locs
+ -- No need to check for duplicate associated type decls
+ -- since that is done by RnNames.extendRdrEnvRn
-- Check the signatures
-- First process the class op sigs (op_sigs), then the fixity sigs (non_op_sigs).
; let sig_rdr_names_w_locs = [op | L _ (TypeSig op _) <- sigs]
- ; checkDupNames sig_doc sig_rdr_names_w_locs
+ ; checkDupRdrNames sig_doc sig_rdr_names_w_locs
-- Typechecker is responsible for checking that we only
-- give default-method bindings for things in this class.
-- The renamer *could* check this for class decls, but can't
; let meth_rdr_names_w_locs = collectHsBindLocatedBinders mbinds
gen_rdr_tyvars_w_locs = [ tv | tv <- extractGenericPatTyVars mbinds,
not (unLoc tv `elemLocalRdrEnv` name_env) ]
- ; checkDupNames meth_doc meth_rdr_names_w_locs
+ -- No need to check for duplicate method signatures
+ -- since that is done by RnNames.extendRdrEnvRn
+ -- and the methods are already in scope
; gen_tyvars <- newLocalsRn gen_rdr_tyvars_w_locs
; rnMethodBinds (unLoc cname') (mkSigTvFn sigs') gen_tyvars mbinds }
returnM (InfixCon new_ty1 new_ty2)
rnConDeclDetails doc (RecCon fields)
- = do { checkDupNames doc (map cd_fld_name fields)
- ; new_fields <- mappM (rnField doc) fields
+ = do { new_fields <- mappM (rnField doc) fields
+ -- No need to check for duplicate fields
+ -- since that is done by RnNames.extendRdrEnvRn
; return (RecCon new_fields) }
rnField doc (ConDeclField name ty haddock_doc)
tcExpr (HsSpliceE splice) res_ty = tcSpliceExpr splice res_ty
tcExpr (HsBracket brack) res_ty = do { e <- tcBracket brack res_ty
; return (unLoc e) }
+tcExpr e@(HsQuasiQuoteE _) res_ty =
+ pprPanic "Should never see HsQuasiQuoteE in type checker" (ppr e)
#endif /* GHCI */
\end{code}
; return (LazyPat pat', [], res) }
+tc_pat _ p@(QuasiQuotePat _) _ _
+ = pprPanic "Should never see QuasiQuotePat in type checker" (ppr p)
+
tc_pat pstate (WildPat _) pat_ty thing_inside
= do { pat_ty' <- unBoxWildCardType pat_ty -- Make sure it's filled in with monotypes
; res <- thing_inside pstate
getGlobalRdrEnv :: TcRn GlobalRdrEnv
getGlobalRdrEnv = do { env <- getGblEnv; return (tcg_rdr_env env) }
+getRdrEnvs :: TcRn (GlobalRdrEnv, LocalRdrEnv)
+getRdrEnvs = do { (gbl,lcl) <- getEnvs; return (tcg_rdr_env gbl, tcl_rdr lcl) }
+
getImports :: TcRn ImportAvails
getImports = do { env <- getGblEnv; return (tcg_imports env) }
tcl_ctxt :: ErrCtxt, -- Error context
tcl_errs :: TcRef Messages, -- Place to accumulate errors
- tcl_th_ctxt :: ThStage, -- Template Haskell context
- tcl_arrow_ctxt :: ArrowCtxt, -- Arrow-notation context
+ tcl_th_ctxt :: ThStage, -- Template Haskell context
+ tcl_arrow_ctxt :: ArrowCtxt, -- Arrow-notation context
tcl_rdr :: LocalRdrEnv, -- Local name envt
-- Maintained during renaming, of course, but also during
-> TcRnTypes.TcM (HsExpr.LHsExpr Var.Id)
tcSpliceDecls :: HsExpr.LHsExpr Name.Name
- -> TcRnTypes.TcM [HsDecls.LHsDecl RdrName.RdrName]
+
+runQuasiQuoteExpr :: HsQuasiQuote Name.Name -> TcM (LHsExpr RdrName)
+runQuasiQuotePat :: HsQuasiQuote Name -> TcM (LPat RdrName)
-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
-- for details
-module TcSplice( tcSpliceExpr, tcSpliceDecls, tcBracket ) where
+module TcSplice( tcSpliceExpr, tcSpliceDecls, tcBracket,
+ runQuasiQuoteExpr, runQuasiQuotePat ) where
#include "HsVersions.h"
kcSpliceType :: HsSplice Name -> TcM (HsType Name, TcKind)
-- None of these functions add constraints to the LIE
+runQuasiQuoteExpr :: HsQuasiQuote Name -> TcM (LHsExpr RdrName)
+runQuasiQuotePat :: HsQuasiQuote Name -> TcM (LPat RdrName)
+
#ifndef GHCI
tcSpliceExpr n e ty = pprPanic "Cant do tcSpliceExpr without GHCi" (ppr e)
tcSpliceDecls e = pprPanic "Cant do tcSpliceDecls without GHCi" (ppr e)
+
+runQuasiQuoteExpr q = pprPanic "Cant do runQuasiQuoteExpr without GHCi" (ppr q)
+runQuasiQuotePat q = pprPanic "Cant do runQuasiQuotePat without GHCi" (ppr q)
#else
\end{code}
%************************************************************************
%* *
+ Quasi-quoting
+%* *
+%************************************************************************
+
+Note [Quasi-quote overview]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The GHC "quasi-quote" extension is described by Geoff Mainland's paper
+"Why it's nice to be quoted: quasiquoting for Haskell" (Haskell
+Workshop 2007).
+
+Briefly, one writes
+ [:p| stuff |]
+and the arbitrary string "stuff" gets parsed by the parser 'p', whose
+type should be Language.Haskell.TH.Quote.QuasiQuoter. 'p' must be
+defined in another module, because we are going to run it here. It's
+a bit like a TH splice:
+ $(p "stuff")
+
+However, you can do this in patterns as well as terms. Becuase of this,
+the splice is run by the *renamer* rather than the type checker.
+
+\begin{code}
+runQuasiQuote :: Outputable hs_syn
+ => HsQuasiQuote Name -- Contains term of type QuasiQuoter, and the String
+ -> Name -- Of type QuasiQuoter -> String -> Q th_syn
+ -> String -- Documentation string only
+ -> Name -- Name of th_syn type
+ -> (SrcSpan -> th_syn -> Either Message hs_syn)
+ -> TcM hs_syn
+runQuasiQuote (HsQuasiQuote name quoter q_span quote) quote_selector desc meta_ty convert
+ = do { -- Check that the quoter is not locally defined, otherwise the TH
+ -- machinery will not be able to run the quasiquote.
+ ; this_mod <- getModule
+ ; let is_local = case nameModule_maybe quoter of
+ Just mod | mod == this_mod -> True
+ | otherwise -> False
+ Nothing -> True
+ ; traceTc (text "runQQ" <+> ppr quoter <+> ppr is_local)
+ ; checkTc (not is_local) (quoteStageError quoter)
+
+ -- Build the expression
+ ; let quoterExpr = L q_span $! HsVar $! quoter
+ ; let quoteExpr = L q_span $! HsLit $! HsString quote
+ ; let expr = L q_span $
+ HsApp (L q_span $
+ HsApp (L q_span (HsVar quote_selector)) quoterExpr) quoteExpr
+ ; recordThUse
+ ; meta_exp_ty <- tcMetaTy meta_ty
+
+ -- Typecheck the expression
+ ; zonked_q_expr <- tcTopSpliceExpr expr meta_exp_ty
+
+ -- Run the expression
+ ; traceTc (text "About to run" <+> ppr zonked_q_expr)
+ ; result <- runMeta convert zonked_q_expr
+ ; traceTc (text "Got result" <+> ppr result)
+ ; showSplice desc zonked_q_expr (ppr result)
+ ; return result
+ }
+
+runQuasiQuoteExpr quasiquote
+ = runQuasiQuote quasiquote quoteExpName "expression" expQTyConName convertToHsExpr
+
+runQuasiQuotePat quasiquote
+ = runQuasiQuote quasiquote quotePatName "pattern" patQTyConName convertToPat
+
+quoteStageError quoter
+ = sep [ptext SLIT("GHC stage restriction:") <+> ppr quoter,
+ nest 2 (ptext SLIT("is used in a quasiquote, and must be imported, not defined locally"))]
+\end{code}
+
+
+%************************************************************************
+%* *
Splicing a type
%* *
%************************************************************************
-> TcM (LHsExpr RdrName)
runMetaE = runMeta
+runMetaP :: (SrcSpan -> TH.Pat -> Either Message (Pat RdrName))
+ -> LHsExpr Id -- Of type (Q Pat)
+ -> TcM (Pat RdrName)
+runMetaP = runMeta
+
runMetaT :: (SrcSpan -> TH.Type -> Either Message (LHsType RdrName))
-> LHsExpr Id -- Of type (Q Type)
-> TcM (LHsType RdrName)
-- encounter them inside the try
--
-- See Note [Exceptions in TH]
- either_tval <- tryAllM $ do
- { th_syn <- TH.runQ (unsafeCoerce# hval)
- ; case convert (getLoc expr) th_syn of
+ let expr_span = getLoc expr
+ ; either_tval <- tryAllM $
+ setSrcSpan expr_span $ -- Set the span so that qLocation can
+ -- see where this splice is
+ do { th_syn <- TH.runQ (unsafeCoerce# hval)
+ ; case convert expr_span th_syn of
Left err -> failWithTc err
Right hs_syn -> return hs_syn }
qReport True msg = addErr (text msg)
qReport False msg = addReport (text msg)
- qCurrentModule = do { m <- getModule;
- return (moduleNameString (moduleName m)) }
- -- ToDo: is throwing away the package name ok here?
-
+ qLocation = do { m <- getModule
+ ; l <- getSrcSpanM
+ ; return (TH.Loc { TH.loc_filename = unpackFS (srcSpanFile l)
+ , TH.loc_module = moduleNameString (moduleName m)
+ , TH.loc_package = packageIdString (modulePackageId m)
+ , TH.loc_start = (srcSpanStartLine l, srcSpanStartCol l)
+ , TH.loc_end = (srcSpanEndLine l, srcSpanEndCol l) }) }
+
qReify v = reify v
-- For qRecover, discard error messages if
\begin{code}
module TcSplice where
-import HsSyn ( HsSplice, HsBracket, HsExpr, LHsExpr, LHsDecl )
+import HsSyn ( HsSplice, HsBracket, HsQuasiQuote,
+ HsExpr, LHsExpr, LPat, LHsDecl )
import Name ( Name )
import RdrName ( RdrName )
import TcRnTypes( TcM, TcId )
-> TcM (LHsExpr TcId)
tcSpliceDecls :: LHsExpr Name -> TcM [LHsDecl RdrName]
+
+runQuasiQuoteExpr :: HsQuasiQuote Name.Name -> TcM (LHsExpr RdrName)
+runQuasiQuotePat :: HsQuasiQuote Name -> TcM (LPat RdrName)
\end{code}
<entry><option>-XNoTemplateHaskell</option></entry>
</row>
<row>
+ <entry><option>-XQuasiQuotes</option></entry>
+ <entry>Enable <link linkend="th-quasiquotation">quasiquotation</link>.</entry>
+ <entry>dynamic</entry>
+ <entry><option>-XNoQuasiQuotes</option></entry>
+ </row>
+ <row>
<entry><option>-XBangPatterns</option></entry>
<entry>Enable <link linkend="bang-patterns">bang patterns</link>.</entry>
<entry>dynamic</entry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-XQuasiQuotes</option></term>
+ <listitem>
+ <para>Enables quasiquotation (see <xref
+ linkend="th-quasiquotation"/>).</para>
+
+ <para>Syntax stolen:
+ <literal>[:<replaceable>varid</replaceable>|</literal>.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect1>
</itemizedlist></para></listitem>
<listitem><para>
+ A quasi-quotation can appear in either a pattern context or an
+ expression context and is also written in Oxford brackets:
+ <itemizedlist>
+ <listitem><para> <literal>[:<replaceable>varid</replaceable>| ... |]</literal>,
+ where the "..." is an arbitrary string; a full description of the
+ quasi-quotation facility is given in <xref linkend="th-quasiquotation"/>.</para></listitem>
+ </itemizedlist></para></listitem>
+
+ <listitem><para>
A name can be quoted with either one or two prefix single quotes:
<itemizedlist>
<listitem><para> <literal>'f</literal> has type <literal>Name</literal>, and names the function <literal>f</literal>.
</orderedlist>
</sect2>
+<sect2 id="th-quasiquotation"> <title> Template Haskell Quasi-quotation </title>
+<para>Quasi-quotation allows patterns and expressions to be written using
+programmer-defined concrete syntax; the motivation behind the extension and
+several examples are documented in
+"<ulink url="http://www.eecs.harvard.edu/~mainland/ghc-quasiquoting/">Why It's
+Nice to be Quoted: Quasiquoting for Haskell</ulink>" (Proc Haskell Workshop
+2007). The example below shows how to write a quasiquoter for a simple
+expression language.</para>
+
+<para>
+In the example, the quasiquoter <literal>expr</literal> is bound to a value of
+type <literal>Language.Haskell.TH.Quote.QuasiQuoter</literal> which contains two
+functions for quoting expressions and patterns, respectively. The first argument
+to each quoter is the (arbitrary) string enclosed in the Oxford brackets. The
+context of the quasi-quotation statement determines which of the two parsers is
+called: if the quasi-quotation occurs in an expression context, the expression
+parser is called, and if it occurs in a pattern context, the pattern parser is
+called.</para>
+
+<para>
+Note that in the example we make use of an antiquoted
+variable <literal>n</literal>, indicated by the syntax <literal>'int:n</literal>
+(this syntax for anti-quotation was defined by the parser's
+author, <emphasis>not</emphasis> by GHC). This binds <literal>n</literal> to the
+integer value argument of the constructor <literal>IntExpr</literal> when
+pattern matching. Please see the referenced paper for further details regarding
+anti-quotation as well as the description of a technique that uses SYB to
+leverage a single parser of type <literal>String -> a</literal> to generate both
+an expression parser that returns a value of type <literal>Q Exp</literal> and a
+pattern parser that returns a value of type <literal>Q Pat</literal>.
+</para>
+
+<para>In general, a quasi-quote has the form
+<literal>[$<replaceable>quoter</replaceable>| <replaceable>string</replaceable> |]</literal>.
+The <replaceable>quoter</replaceable> must be the name of an imported quoter; it
+cannot be an arbitrary expression. The quoted <replaceable>string</replaceable>
+can be arbitrary, and may contain newlines.
+</para>
+<para>
+Quasiquoters must obey the same stage restrictions as Template Haskell, e.g., in
+the example, <literal>expr</literal> cannot be defined
+in <literal>Main.hs</literal> where it is used, but must be imported.
+</para>
+
+<programlisting>
+
+{- Main.hs -}
+module Main where
+
+import Expr
+
+main :: IO ()
+main = do { print $ eval [$expr|1 + 2|]
+ ; case IntExpr 1 of
+ { [$expr|'int:n|] -> print n
+ ; _ -> return ()
+ }
+ }
+
+
+{- Expr.hs -}
+module Expr where
+
+import qualified Language.Haskell.TH as TH
+import Language.Haskell.TH.Quasi
+
+data Expr = IntExpr Integer
+ | AntiIntExpr String
+ | BinopExpr BinOp Expr Expr
+ | AntiExpr String
+ deriving(Show, Typeable, Data)
+
+data BinOp = AddOp
+ | SubOp
+ | MulOp
+ | DivOp
+ deriving(Show, Typeable, Data)
+
+eval :: Expr -> Integer
+eval (IntExpr n) = n
+eval (BinopExpr op x y) = (opToFun op) (eval x) (eval y)
+ where
+ opToFun AddOp = (+)
+ opToFun SubOp = (-)
+ opToFun MulOp = (*)
+ opToFun DivOp = div
+
+expr = QuasiQuoter parseExprExp parseExprPat
+
+-- Parse an Expr, returning its representation as
+-- either a Q Exp or a Q Pat. See the referenced paper
+-- for how to use SYB to do this by writing a single
+-- parser of type String -> Expr instead of two
+-- separate parsers.
+
+parseExprExp :: String -> Q Exp
+parseExprExp ...
+
+parseExprPat :: String -> Q Pat
+parseExprPat ...
+</programlisting>
+
+<para>Now run the compiler:
+</para>
+<programlisting>
+$ ghc --make -XQuasiQuotes Main.hs -o main
+</programlisting>
+
+<para>Run "main" and here is your output:</para>
+
+<programlisting>
+$ ./main
+3
+1
+</programlisting>
+
+</sect2>
+
</sect1>
<!-- ===================== Arrow notation =================== -->