{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -- | Quasiquoters for easier generation of STG syntax trees. -- The 'stg' quoter is most convenient, I suggest you use it unless you have a -- reason not to. module Stg.Parser.QuasiQuoter ( -- * Heuristic quasiquoter stg, -- * Specific syntax element quasiquoters program, binds, lambdaForm, expr, alts, nonDefaultAlts, algebraicAlt, primitiveAlt, defaultAlt, literal, primOp, atom, ) where import Data.Either import Data.Monoid import Data.Text (Text) import qualified Data.Text as T import Language.Haskell.TH import Language.Haskell.TH.Lift import Language.Haskell.TH.Quote import Text.PrettyPrint.ANSI.Leijen hiding ((<>)) import Stg.Language.Prettyprint import Stg.Parser.Parser (StgParser, parse) import qualified Stg.Parser.Parser as Parser -- $setup -- >>> :set -XTemplateHaskell -- >>> :set -XQuasiQuotes defaultQuoter :: QuasiQuoter defaultQuoter = QuasiQuoter { quoteExp = \_ -> fail "No STG expression quoter implemented" , quotePat = \_ -> fail "No STG pattern quoter implemented" , quoteType = \_ -> fail "No STG type quoter implemented" , quoteDec = \_ -> fail "No STG declaration quoter implemented" } -- | Heuristic quasiquoter for STG language elements. -- Tries a number of parsers, and will use the first successful one. -- -- To gain more fine-grained control over what the input should be parsed to, -- use one of the non-heuristic quoters, such as 'stgProgram' or -- 'stgLambdaForm'. These will also give you much better error messages than -- merely "doesn't work". -- -- >>> [stg| id = \x -> x |] -- Program (Binds [(Var "id",LambdaForm [] NoUpdate [Var "x"] (AppF (Var "x") []))]) -- -- >>> [stg| \x -> x |] -- LambdaForm [] NoUpdate [Var "x"] (AppF (Var "x") []) -- -- >>> [stg| x |] -- AppF (Var "x") [] stg :: QuasiQuoter stg = defaultQuoter { quoteExp = expQuoter } where expQuoter inputString = let input = T.pack inputString parses = [ quoteAs "program" Parser.program input , quoteAs "lambdaForm" Parser.lambdaForm input , quoteAs "expr" Parser.expr input , quoteAs "alts" Parser.alts input , quoteAs "algebraicAlt" Parser.algebraicAlt input , quoteAs "primitiveAlt" Parser.primitiveAlt input , quoteAs "defaultAlt" Parser.defaultAlt input , quoteAs "literal" Parser.literal input , quoteAs "primOp" Parser.primOp input , quoteAs "atom" Parser.atom input , quoteAs "variable" Parser.var input , quoteAs "constructor" Parser.con input ] in case partitionEithers parses of (_, ast:_) -> ast (errs, _) -> (fail . T.unpack . T.unlines) ("No parse succeeded. Individual errors:" : errs) -- | Attempt to parse an input using a certain parser, and return the -- generated expression on success. quoteAs :: Lift ast => Text -> Parser.StgParser ast -> Text -> Either Text (Q Exp) quoteAs parserName parser input = fmap lift (case Parser.parse parser input of Left err -> Left (prettyprintOldAnsi (" -" <+> text (T.unpack parserName) <> ":" <+> plain (align err))) Right r -> Right r ) -- | Build a quasiquoter from a 'Parser'. stgQQ :: Lift ast => StgParser ast -> Text -- ^ Name of the parsed syntax element (for error reporting) -> QuasiQuoter stgQQ parser elementName = defaultQuoter { quoteExp = expQuoter } where expQuoter input = case parse parser (T.pack input) of Left err -> fail (T.unpack ("Invalid STG " <> elementName <> ":\n" <> prettyprintOldAnsi (plain err))) Right ast -> [| ast |] -- | Quasiquoter for 'Stg.Language.Program's. -- -- >>> [program| id = \x -> x |] -- Program (Binds [(Var "id",LambdaForm [] NoUpdate [Var "x"] (AppF (Var "x") []))]) program :: QuasiQuoter program = stgQQ Parser.program "program" -- | Quasiquoter for 'Stg.Language.Binds'. -- -- >>> [binds| id = \x -> x |] -- (Binds [(Var "id",LambdaForm [] NoUpdate [Var "x"] (AppF (Var "x") []))]) binds :: QuasiQuoter binds = stgQQ Parser.binds "binds" -- | Quasiquoter for 'Stg.Language.LambdaForm's. -- -- >>> [lambdaForm| \x -> x |] -- LambdaForm [] NoUpdate [Var "x"] (AppF (Var "x") []) lambdaForm :: QuasiQuoter lambdaForm = stgQQ Parser.lambdaForm "lambda form" -- | Quasiquoter for 'Stg.Language.Expr'essions. -- -- >>> [expr| f x y z |] -- AppF (Var "f") [AtomVar (Var "x"),AtomVar (Var "y"),AtomVar (Var "z")] expr :: QuasiQuoter expr = stgQQ Parser.expr "expression" -- | Quasiquoter for 'Stg.Language.Alts'. -- -- >>> [alts| Just x -> True; default -> False |] -- Alts (AlgebraicAlts (AlgebraicAlt (Constr "Just") [Var "x"] (AppC (Constr "True") []) :| [])) (DefaultNotBound (AppC (Constr "False") [])) -- -- >>> [alts| 0# -> True; default -> False |] -- Alts (PrimitiveAlts (PrimitiveAlt (Literal 0) (AppC (Constr "True") []) :| [])) (DefaultNotBound (AppC (Constr "False") [])) alts :: QuasiQuoter alts = stgQQ Parser.alts "alternatives" -- | Quasiquoter for 'Stg.Language.Alt'. -- -- >>> [nonDefaultAlts| Just x -> True; Nothing -> False; |] -- AlgebraicAlts (AlgebraicAlt (Constr "Just") [Var "x"] (AppC (Constr "True") []) :| [AlgebraicAlt (Constr "Nothing") [] (AppC (Constr "False") [])]) -- -- >>> [nonDefaultAlts| 0# -> False; 1# -> True; |] -- PrimitiveAlts (PrimitiveAlt (Literal 0) (AppC (Constr "False") []) :| [PrimitiveAlt (Literal 1) (AppC (Constr "True") [])]) nonDefaultAlts :: QuasiQuoter nonDefaultAlts = stgQQ Parser.nonDefaultAlts "algebraic alternatives" -- | Quasiquoter for 'Stg.Language.AlgebraicAlt's. -- -- >>> [algebraicAlt| Just x -> x; |] -- AlgebraicAlt (Constr "Just") [Var "x"] (AppF (Var "x") []) algebraicAlt :: QuasiQuoter algebraicAlt = stgQQ Parser.algebraicAlt "algebraic alternative" -- | Quasiquoter for 'Stg.Language.PrimitiveAlt's. -- -- >>> [primitiveAlt| 1# -> x; |] -- PrimitiveAlt (Literal 1) (AppF (Var "x") []) primitiveAlt :: QuasiQuoter primitiveAlt = stgQQ Parser.primitiveAlt "primitive alternative" -- | Quasiquoter for 'Stg.Language.DefaultAlt's. -- -- >>> [defaultAlt| default -> x |] -- DefaultNotBound (AppF (Var "x") []) -- -- >>> [defaultAlt| x -> x |] -- DefaultBound (Var "x") (AppF (Var "x") []) defaultAlt :: QuasiQuoter defaultAlt = stgQQ Parser.defaultAlt "default alternative" -- | Quasiquoter for 'Stg.Language.Literal's. -- -- >>> [literal| 1# |] -- Literal 1 literal :: QuasiQuoter literal = stgQQ Parser.literal "literal" -- | Quasiquoter for 'Stg.Language.PrimOp's. -- -- >>> [primOp| +# |] -- Add primOp :: QuasiQuoter primOp = stgQQ Parser.primOp "primop" -- | Quasiquoter for 'Stg.Language.Atom's. -- -- >>> [atom| x |] -- AtomVar (Var "x") atom :: QuasiQuoter atom = stgQQ Parser.atom "atom"