{-| Module : Language.Rust.Quote Description : Quasiquotes for Rust AST Copyright : (c) Alec Theriault, 2017-2018 License : BSD-style Maintainer : alec.theriault@gmail.com Stability : experimental Portability : GHC Quasiquoters for converting Rust code into the equivalent Haskell patterns and expressions. These are just convenience wrappers over 'dataToExpQ' and 'dataToPatQ'. These quasiquoters only work as expressions and patterns, not as declarations or types. The pattern quasiquoters recursively ignore any 'Span' or 'Position' fields (replacing them with wildcard patterns). Using quasiquotes instead of manually writing out the AST means that even if the AST evolves (perhaps by adding extra fields to certain nodes), your code is likely to continue to work. The examples below assume the following GHCi flag and import: >>> :set -XQuasiQuotes >>> import Control.Monad ( void ) -} {-# LANGUAGE RankNTypes, QuasiQuotes, TemplateHaskell #-} module Language.Rust.Quote ( lit, attr, ty, pat, stmt, expr, item, sourceFile, implItem, traitItem, tokenTree, block, stmts, items, implItems ) where {- In the future, we may try to do something similar to Rust macros to extract or inject ASTs out or into the quasiquotes. Eventually, one should be able to just import this module for code generation. The following interaction is what should eventually work. >>> import qualified Language.Rust.Quote as Q >>> :set -XQuasiQuotes +t >>> let one = [Q.expr| 1i32 |] one :: Expr Span >>> [Q.expr| |x: i32| -> $retTy:ty $body:block |] = [Q.expr| |x: i32| -> i32 { ($one) + x } |] retTy :: Ty Span body :: Block Span >>> import Language.Rust.Pretty >>> pretty retTy i32 >>> pretty body { (1i32) + x } For now, however, you cannot use @$x@ or @$x:ty@ meta variables. -} import Language.Rust.Parser.ParseMonad import Language.Rust.Parser.Internal import Language.Rust.Data.InputStream ( inputStreamFromString ) import Language.Rust.Data.Position ( Position(..), Span ) import Language.Rust.Data.Ident ( Ident(..), IdentName(..), mkIdent ) import Language.Rust.Syntax.AST ( Expr(EmbeddedExpr), TokenTree(..) ) import Language.Rust.Syntax.Token ( Token(..) ) import Language.Haskell.TH.Syntax ( liftData ) -- liftData :: Data a => a -> Q Exp import Language.Haskell.TH import Language.Haskell.TH.Quote ( QuasiQuoter(..), dataToExpQ, dataToPatQ ) import qualified Language.Haskell.Meta as LHM import Control.Applicative ( (<|>) ) import Control.Monad ( (>=>) ) import Data.Functor ( ($>) ) import Data.Typeable ( cast, Typeable ) import Data.Data ( Data ) -- | Given a parser, convert it into a quasiquoter. The quasiquoter produced does not support -- declarations and types. For patterns, it replaces any 'Span' and 'Position' field with a -- wild pattern. quoter :: Data a => (forall b. Typeable b => b -> Maybe (Q Exp)) -> P a -> QuasiQuoter --quoter :: P ExpQ -> QuasiQuoter quoter exprTransformer p = QuasiQuoter { quoteExp = parse >=> dataToExpQ exprTransformer , quotePat = parse >=> dataToPatQ wildSpanPos , quoteDec = error "this quasiquoter does not support declarations" , quoteType = error "this quasiquoter does not support types" } where -- | Given a parser and an input string, turn it into the corresponding Haskell expression/pattern. parse inp = do Loc{ loc_start = (r,c) } <- location -- Run the parser case execParser p (inputStreamFromString inp) (Position 0 r c) of Left (ParseFail _ msg) -> fail msg Right x -> pure x -- | Replace 'Span' and 'Position' with wild patterns wildSpanPos :: Typeable b => b -> Maybe (Q Pat) wildSpanPos x = ((cast x :: Maybe Span) $> wildP) <|> ((cast x :: Maybe Position) $> wildP) castExpr :: Typeable b => b -> Maybe (String, Q Exp -> Q Exp) castExpr b = do (EmbeddedExpr _ code _) <- cast b :: Maybe (Expr Span) pure (code, id) castIdent :: Typeable b => b -> Maybe (String, Q Exp -> Q Exp) castIdent b = do (Ident (HaskellName code) _ _) <- cast b :: Maybe Ident pure (code, (\e -> [| mkIdent $(e) |])) castTokenIDENT :: Typeable b => b -> Maybe (String, Q Exp -> Q Exp) castTokenIDENT b = do (Token spn (EmbeddedIdent code)) <- cast b :: Maybe TokenTree pure (code, \e -> [| Token $(liftData spn) (IdentTok $ mkIdent $(e)) |]) castTokenEXPR :: Typeable b => b -> Maybe (String, Q Exp -> Q Exp) castTokenEXPR b = do (Token spn (EmbeddedCode code)) <- cast b :: Maybe TokenTree pure (code, \e -> [| Token $(liftData spn) $(e) |]) --(EmbeddedExpr _ code _) <- (cast b :: Maybe (Expr Span)) embeddedCode :: Typeable b => b -> Maybe (Q Exp) embeddedCode b = do (code, tsfm) <- castExpr b <|> castIdent b <|> castTokenIDENT b <|> castTokenEXPR b pure $ case LHM.parseExp code of Left err -> error $ "Could not parse embedded Haskell code: " ++ err Right expTH -> tsfm $ return expTH -- | Quasiquoter for literals (see 'Language.Rust.Syntax.Lit'). -- -- >>> void [lit| 1.4e29f64 |] -- Float 1.4e29 F64 () -- lit :: QuasiQuoter lit = quoter (const Nothing) parseLit -- | Quasiquoter for attributes (see 'Language.Rust.Syntax.Attribute') -- -- >>> void [attr| #[no_mangle] |] -- Attribute Outer (Path False [PathSegment "no_mangle" Nothing ()] ()) (Stream []) () -- attr :: QuasiQuoter attr = quoter embeddedCode parseAttr -- | Quasiquoter for types (see 'Language.Rust.Syntax.Ty') -- -- >>> void [ty| &(_,_) |] -- Rptr Nothing Immutable (TupTy [Infer (),Infer ()] ()) () -- ty :: QuasiQuoter ty = quoter embeddedCode parseTy -- | Quasiquoter for patterns (see 'Language.Rust.Syntax.Pat') -- -- >>> void [pat| x @ 1...5 |] -- IdentP (ByValue Immutable) "x" (Just (RangeP (Lit [] (Int Dec 1 Unsuffixed ()) ()) -- (Lit [] (Int Dec 5 Unsuffixed ()) ()) ())) () -- pat :: QuasiQuoter pat = quoter embeddedCode parsePat -- | Quasiquoter for statements (see 'Language.Rust.Syntax.Stmt') -- -- >>> void [stmt| let x = 4i32; |] -- Local (IdentP (ByValue Immutable) "x" Nothing ()) Nothing (Just (Lit [] (Int Dec 4 I32 ()) ())) [] () -- stmt :: QuasiQuoter stmt = quoter embeddedCode parseStmt stmts :: QuasiQuoter stmts = quoter embeddedCode parseStmts -- | Quasiquoter for expressions (see 'Language.Rust.Syntax.Expr') -- -- >>> void [expr| (x,) |] -- TupExpr [] [PathExpr [] Nothing (Path False [PathSegment "x" Nothing ()] ()) ()] () -- expr :: QuasiQuoter expr = quoter embeddedCode parseExpr -- | Quasiquoter for items (see 'Language.Rust.Syntax.Item') -- -- >>> void [item| type Unit = (); |] -- TyAlias [] InheritedV "Unit" (TupTy [] ()) (Generics [] [] (WhereClause [] ()) ()) () -- item :: QuasiQuoter item = quoter embeddedCode parseItem items :: QuasiQuoter items = quoter embeddedCode parseItems -- | Quasiquoter for a whole source file (see 'Language.Rust.Syntax.SourceFile') -- -- >>> void [sourceFile| fn main() { } |] -- SourceFile Nothing [] [Fn [] InheritedV "main" -- (FnDecl [] Nothing False ()) -- Normal NotConst Rust -- (Generics [] [] (WhereClause [] ()) ()) -- (Block [] Normal ()) ()] -- sourceFile :: QuasiQuoter sourceFile = quoter embeddedCode parseSourceFile -- | Quasiquoter for blocks (see 'Language.Rust.Syntax.Block') -- -- >>> void [block| unsafe { 1i32 } |] -- Block [NoSemi (Lit [] (Int Dec 1 I32 ()) ()) ()] Unsafe () -- block :: QuasiQuoter block = quoter embeddedCode parseBlock -- | Quasiquoter for impl items (see 'Language.Rust.Syntax.ImplItem') -- -- >>> void [implItem| type Item = (); |] -- TypeI [] InheritedV Final "Item" (TupTy [] ()) () -- implItem :: QuasiQuoter implItem = quoter embeddedCode parseImplItem implItems :: QuasiQuoter implItems = quoter embeddedCode parseImplItems -- | Quasiquoter for trait items (see 'Language.Rust.Syntax.TraitItem') -- -- >>> void [traitItem| type Item; |] -- TypeT [] "Item" [] Nothing () -- traitItem :: QuasiQuoter traitItem = quoter embeddedCode parseTraitItem -- | Quasiquoter for token trees (see 'Language.Rust.Syntax.TokenTree') -- -- >>> [tokenTree| fn |] -- Token (Span (Position 1 2 14) (Position 3 2 16)) fn -- tokenTree :: QuasiQuoter tokenTree = quoter embeddedCode parseTt