diff --git a/language-python/language-python.cabal b/language-python/language-python.cabal index 72d7409..b75b593 100644 --- a/language-python/language-python.cabal +++ b/language-python/language-python.cabal @@ -33,7 +33,9 @@ Library array >= 0.4 && < 0.6, transformers >= 0.3 && < 0.6, monads-tf == 0.1.*, - utf8-string >= 1 && < 2 + utf8-string >= 1 && < 2, + template-haskell >=2.10 + build-tools: happy, alex exposed-modules: Language.Python.Common @@ -45,6 +47,7 @@ Library Language.Python.Common.PrettyToken Language.Python.Common.AST Language.Python.Common.PrettyAST + Language.Python.Common.Quoter Language.Python.Version3 Language.Python.Version3.Parser Language.Python.Version3.Lexer diff --git a/language-python/src/Language/Python/Common.hs b/language-python/src/Language/Python/Common.hs index 871a434..1022be0 100644 --- a/language-python/src/Language/Python/Common.hs +++ b/language-python/src/Language/Python/Common.hs @@ -27,7 +27,9 @@ module Language.Python.Common ( -- * Parse errors module Language.Python.Common.ParseError, -- * Pretty printing parse errors - module Language.Python.Common.PrettyParseError -- this export is for Haddock + module Language.Python.Common.PrettyParseError, -- this export is for Haddock + -- * Quasiquoter + module Language.Python.Common.Quoter ) where import Language.Python.Common.Pretty @@ -38,3 +40,4 @@ import Language.Python.Common.PrettyToken () import Language.Python.Common.SrcLocation import Language.Python.Common.PrettyParseError () import Language.Python.Common.ParseError +import Language.Python.Common.Quoter diff --git a/language-python/src/Language/Python/Common/Quoter.hs b/language-python/src/Language/Python/Common/Quoter.hs new file mode 100644 index 0000000..b75946f --- /dev/null +++ b/language-python/src/Language/Python/Common/Quoter.hs @@ -0,0 +1,106 @@ +----------------------------------------------------------------------------- +-- | +-- Module : Language.Python.Common.Quoter +-- License : BSD-style +-- Stability : experimental +-- Portability : ghc +-- +-- Simple quasiquoters for converting python code into haskell expressions or patterns (it doesn't work for declarations and types). +-- Multi-line python quotations must be left aligned in the file. +-- Examples, that will yield : +-- @ +-- binOpExpr = [pyExpr|x+y|] +-- +-- [retStmt] = [pyStmt|return z|] +-- +-- [py2Print] = [py2] = [py2Stmt|print somelist[1], "Hej there!"|] +-- +-- pyMod = [pyModule| +-- import something +-- +-- def fun(a,b): +-- c = a+b +-- print(c) +-- |] +-- @ +-- Caution: This checks only for syntax of single quotes, not types or scopes or combinations of quotes. +-- For example nothing keeps you from appending py2Print to pyMod. So this can be used to generate code, +-- but checking validity is up to you. +----------------------------------------------------------------------------- + +module Language.Python.Common.Quoter ( + -- * Quoting python 3.x modules + pyModule, + -- * Quoting python 3.x statements + pyStmt, + -- * Quoting python 3.x expressions + pyExpr, + -- * Quoting python 2.x modules + py2Module, + -- * Quoting python 2.x statements + py2Stmt, + -- * Quoting python 2.x expressions + py2Expr) where + +import Language.Python.Version3.Parser as V3 +import Language.Python.Version2.Parser as V2 + +import Language.Python.Common.Token as Token +import Language.Python.Common.ParserMonad ( ParseError) +import Data.Data ( Data ) + +import Language.Haskell.TH +import Language.Haskell.TH.Quote ( QuasiQuoter(..), dataToExpQ, dataToPatQ ) + +quoter :: (Data a) => (String -> String -> Either ParseError (a, [Token]))-> QuasiQuoter +quoter parser = QuasiQuoter + { quoteExp = parseAndExpQuote parser + , quotePat = parseAndPatQuote parser + , quoteDec = error "this quasiquoter does not support declarations" + , quoteType = error "this quasiquoter does not support types" + } + + +parseAndExpQuote:: (Data a) => (String -> String -> Either ParseError (a, [Token])) -> String -> Q Exp +parseAndExpQuote parser content = do + parseResult <- apply parser "" content + dataToExpQ (const Nothing) parseResult + +parseAndPatQuote:: (Data a) => (String -> String -> Either ParseError (a, [Token]))-> String -> Q Pat +parseAndPatQuote parser content = do + parseResult <- apply parser "" content + dataToPatQ (const Nothing) parseResult + +apply:: (Monad m, MonadFail m) => + (String -> String -> Either ParseError (a, [Token])) + -> String -> String -> m a +apply parser name content = + case parser content name of + Left parseError -> fail $ show parseError + Right (subAST, comments) -> return subAST + + +pyModule :: QuasiQuoter +pyModule = quoter V3.parseModule + + +pyStmt :: QuasiQuoter +pyStmt = quoter V3.parseStmt + + +pyExpr:: QuasiQuoter +pyExpr = quoter V3.parseExpr + + +py2Module :: QuasiQuoter +py2Module = quoter V2.parseModule + + +py2Stmt :: QuasiQuoter +py2Stmt = quoter V2.parseStmt + + +py2Expr:: QuasiQuoter +py2Expr = quoter V2.parseExpr + + diff --git a/language-python/src/Language/Python/Version3.hs b/language-python/src/Language/Python/Version3.hs index 1038857..cc0544b 100644 --- a/language-python/src/Language/Python/Version3.hs +++ b/language-python/src/Language/Python/Version3.hs @@ -23,8 +23,8 @@ module Language.Python.Version3 ( -- * The parser module Language.Python.Version3.Parser, -- * The lexer - module Language.Python.Version3.Lexer + module Language.Python.Version3.Lexer ) where import Language.Python.Version3.Parser -import Language.Python.Version3.Lexer +import Language.Python.Version3.Lexer