From b601a1601d6b9a9c2033d9c027ffa54e9c374a19 Mon Sep 17 00:00:00 2001 From: Lisza Date: Thu, 29 Jul 2021 19:41:13 +0200 Subject: [PATCH 1/3] adding simple quoter there is now a quasi quoter for expressions (modules, statements, python expression) in version 3 Todo: parser is initializied at location 0,0, hence multi-line quotes need to be left-aligned -> todo: use internal parser initialization and set start location --- language-python/language-python.cabal | 5 +- .../src/Language/Python/Version3.hs | 5 +- .../src/Language/Python/Version3/Quoter.hs | 52 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 language-python/src/Language/Python/Version3/Quoter.hs diff --git a/language-python/language-python.cabal b/language-python/language-python.cabal index 72d7409..1b6bcbc 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 @@ -48,6 +50,7 @@ Library Language.Python.Version3 Language.Python.Version3.Parser Language.Python.Version3.Lexer + Language.Python.Version3.Quoter Language.Python.Version2 Language.Python.Version2.Parser Language.Python.Version2.Lexer diff --git a/language-python/src/Language/Python/Version3.hs b/language-python/src/Language/Python/Version3.hs index 1038857..dca210c 100644 --- a/language-python/src/Language/Python/Version3.hs +++ b/language-python/src/Language/Python/Version3.hs @@ -23,8 +23,11 @@ module Language.Python.Version3 ( -- * The parser module Language.Python.Version3.Parser, -- * The lexer - module Language.Python.Version3.Lexer + module Language.Python.Version3.Lexer, + -- * The quasiquoter + module Language.Python.Version3.Quoter ) where import Language.Python.Version3.Parser import Language.Python.Version3.Lexer +import Language.Python.Version3.Quoter diff --git a/language-python/src/Language/Python/Version3/Quoter.hs b/language-python/src/Language/Python/Version3/Quoter.hs new file mode 100644 index 0000000..9712d24 --- /dev/null +++ b/language-python/src/Language/Python/Version3/Quoter.hs @@ -0,0 +1,52 @@ +module Language.Python.Version3.Quoter ( + -- * Quoting modules + pyModule, + -- * Quoting statements + pyStmt, + -- * Quoting expressions + pyExpr) where + +import Language.Python.Version3.Parser as Parser + +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 = parseAndLift parser "" + , quotePat = error "this quasiquoter does not support patterns" + , quoteDec = error "this quasiquoter does not support declarations" + , quoteType = error "this quasiquoter does not support types" + } + + +parseAndLift:: (Data a) => (String -> String -> Either ParseError (a, [Token])) -> String -> String -> Q Exp +parseAndLift parser name content = do + parseResult <- apply parser name content + dataToExpQ (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 Parser.parseModule + + +pyStmt :: QuasiQuoter +pyStmt = quoter Parser.parseStmt + + +pyExpr:: QuasiQuoter +pyExpr = quoter Parser.parseExpr + + From 8030b3be20313e040dc687c0e0aa76a815f54037 Mon Sep 17 00:00:00 2001 From: Lisza Date: Fri, 30 Jul 2021 17:54:40 +0200 Subject: [PATCH 2/3] common quoter for 2.x and 3.x --- language-python/language-python.cabal | 2 +- language-python/src/Language/Python/Common.hs | 5 +- .../src/Language/Python/Common/Quoter.hs | 87 +++++++++++++++++++ .../src/Language/Python/Version3.hs | 7 +- .../src/Language/Python/Version3/Quoter.hs | 52 ----------- 5 files changed, 94 insertions(+), 59 deletions(-) create mode 100644 language-python/src/Language/Python/Common/Quoter.hs delete mode 100644 language-python/src/Language/Python/Version3/Quoter.hs diff --git a/language-python/language-python.cabal b/language-python/language-python.cabal index 1b6bcbc..b75b593 100644 --- a/language-python/language-python.cabal +++ b/language-python/language-python.cabal @@ -47,10 +47,10 @@ 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 - Language.Python.Version3.Quoter Language.Python.Version2 Language.Python.Version2.Parser Language.Python.Version2.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..c16c659 --- /dev/null +++ b/language-python/src/Language/Python/Common/Quoter.hs @@ -0,0 +1,87 @@ +----------------------------------------------------------------------------- +-- | +-- 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. +----------------------------------------------------------------------------- + +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 dca210c..cc0544b 100644 --- a/language-python/src/Language/Python/Version3.hs +++ b/language-python/src/Language/Python/Version3.hs @@ -23,11 +23,8 @@ module Language.Python.Version3 ( -- * The parser module Language.Python.Version3.Parser, -- * The lexer - module Language.Python.Version3.Lexer, - -- * The quasiquoter - module Language.Python.Version3.Quoter + module Language.Python.Version3.Lexer ) where import Language.Python.Version3.Parser -import Language.Python.Version3.Lexer -import Language.Python.Version3.Quoter +import Language.Python.Version3.Lexer diff --git a/language-python/src/Language/Python/Version3/Quoter.hs b/language-python/src/Language/Python/Version3/Quoter.hs deleted file mode 100644 index 9712d24..0000000 --- a/language-python/src/Language/Python/Version3/Quoter.hs +++ /dev/null @@ -1,52 +0,0 @@ -module Language.Python.Version3.Quoter ( - -- * Quoting modules - pyModule, - -- * Quoting statements - pyStmt, - -- * Quoting expressions - pyExpr) where - -import Language.Python.Version3.Parser as Parser - -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 = parseAndLift parser "" - , quotePat = error "this quasiquoter does not support patterns" - , quoteDec = error "this quasiquoter does not support declarations" - , quoteType = error "this quasiquoter does not support types" - } - - -parseAndLift:: (Data a) => (String -> String -> Either ParseError (a, [Token])) -> String -> String -> Q Exp -parseAndLift parser name content = do - parseResult <- apply parser name content - dataToExpQ (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 Parser.parseModule - - -pyStmt :: QuasiQuoter -pyStmt = quoter Parser.parseStmt - - -pyExpr:: QuasiQuoter -pyExpr = quoter Parser.parseExpr - - From 66da2b9ee12519eeed0f781a5eed72acd1062c08 Mon Sep 17 00:00:00 2001 From: Lisza Date: Wed, 4 Aug 2021 14:19:27 +0200 Subject: [PATCH 3/3] usage examples --- .../src/Language/Python/Common/Quoter.hs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/language-python/src/Language/Python/Common/Quoter.hs b/language-python/src/Language/Python/Common/Quoter.hs index c16c659..b75946f 100644 --- a/language-python/src/Language/Python/Common/Quoter.hs +++ b/language-python/src/Language/Python/Common/Quoter.hs @@ -7,6 +7,25 @@ -- -- 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 (