Created
February 13, 2015 23:36
-
-
Save aavogt/906cfc6468b258e966aa to your computer and use it in GitHub Desktop.
(incomplete) Haskell Parser done with open recursion & uu-parsinglib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# OPTIONS_GHC -fno-warn-missing-fields #-} | |
{-# LANGUAGE FlexibleContexts #-} | |
{-# LANGUAGE RankNTypes #-} | |
module Main where | |
import Data.Default.Class | |
import Text.ParserCombinators.UU | |
import Text.ParserCombinators.UU.Idioms | |
import Text.ParserCombinators.UU.BasicInstances | |
import Text.ParserCombinators.UU.Utils hiding (pLBrace, pRBrace) | |
import Data.Char | |
import Data.List | |
import System.Environment | |
import Text.Read | |
import Data.Maybe | |
import Data.List.Split | |
import Test.Hspec | |
import Language.Haskell.TH.Ppr (Ppr(ppr)) | |
import Language.Haskell.TH.PprLib (Doc) | |
import Language.Haskell.TH.Syntax | |
(Type(..), | |
Dec(..), | |
Exp(..), | |
Pat(..), | |
Name(..), | |
OccName(..), | |
NameFlavour(..), | |
ModName(..), | |
NameSpace(..), | |
PkgName(..), | |
TyVarBndr(..), | |
Cxt(..), | |
Pred(..), | |
Kind, | |
TyLit(..), | |
FieldExp(..), | |
Match(..), | |
Lit(..), | |
Guard(..), | |
Stmt(..), Range(..), | |
Body(..), | |
Clause(..), | |
FieldPat(..), | |
) | |
-- do something like | |
-- https://github.com/luqui/parsec-layout/blob/master/Text/Parsec/Layout.hs | |
-- XXX extra parens | |
defHP = def :: HParser | |
runP :: Show t => (HParser -> HP t) -> String -> String | |
runP p str = case runParser "p" (fixP (\d -> amb . p d) defHP) str of | |
[e] -> show e | |
es -> error $ "str:" ++ str ++ " parses as " ++ show (map show es) | |
runPp :: Ppr t => (HParser -> HP t) -> String -> String | |
runPp p str = case runParser "p" (fixP (\d -> amb . p d) defHP) str of | |
[e] -> show (ppr e) | |
es -> error $ "str:" ++ str ++ " parses as " ++ show (map ppr es) | |
runPpAmb :: Ppr t => (HParser -> HP t) -> String -> Doc | |
runPpAmb p str = ppr $ runParser "p" (fixP (\d -> amb . p d) defHP) str | |
runPAmb :: Show t => (HParser -> HP t) -> String -> String | |
runPAmb p str = show $ runParser "p" (fixP (\d -> amb . p d) defHP) str | |
main = hspec $ do | |
let sid f str = f str `shouldBe` str | |
it "AppT" $ do | |
runP pType "Ab B" `shouldBe` "AppT (ConT Ab) (ConT B)" | |
runP pType "A B C" `shouldBe` "AppT (AppT (ConT A) (ConT B)) (ConT C)" | |
runP pType "A B C D" `shouldBe` "AppT (AppT (AppT (ConT A) (ConT B)) (ConT C)) (ConT D)" | |
it "AppE" $ do | |
runP pExp "A B" `shouldBe` "AppE (ConE A) (ConE B)" | |
runP pExp "A B C" `shouldBe` "AppE (AppE (ConE A) (ConE B)) (ConE C)" | |
it "UInfix" $ do | |
runP pExp "A + B + C D" `shouldBe` "UInfixE (UInfixE (ConE A) (VarE +) (ConE B)) (VarE +) (AppE (ConE C) (ConE D))" | |
runPp pExp `sid` "A `b` C" | |
runPp pExp "A b`b`C" `shouldBe` "A b `b` C" | |
it "PromotedT" $ do | |
runPp pExp `sid` "Proxy :: Proxy 'True" | |
runPp pExp `sid` "Proxy :: Proxy ('True :: Bool)" | |
runPp pExp "Proxy :: Proxy '(Int,String)" `shouldBe` "Proxy :: Proxy ('(Int, String))" | |
runPp pExp "undefined :: HList [Int,String,Double]" | |
`shouldBe` "undefined :: HList ((':) Int ((':) String ((':) Double '[])))" | |
runPp pExp "x :: '[Int]" `shouldBe` "x :: (':) Int '[]" | |
runP pExp "Proxy :: Proxy '(x,y)" `shouldBe` "SigE (ConE Proxy) (AppT (ConT Proxy) (AppT (AppT (PromotedTupleT 2) (VarT x)) (VarT y)))" | |
it "ConT Infix" $ do | |
runP pType "A :+: B" `shouldBe` "AppT (AppT (ConT :+:) (ConT A)) (ConT B)" | |
it "ForallT" $ do | |
runP pType "Show a => a" `shouldBe` "ForallT [] [ClassP Show [VarT a]] (VarT a)" | |
runP pType "(a ~ b) => a" `shouldBe` "ForallT [] [EqualP (VarT a) (VarT b)] (VarT a)" | |
runP pType "(Show a) => a" `shouldBe` "ForallT [] [ClassP Show [VarT a]] (VarT a)" | |
runP pType "forall a. (Show a) => a" `shouldBe` "ForallT [PlainTV a] [ClassP Show [VarT a]] (VarT a)" | |
it "SigT" $ do | |
-- ghc bug #10050 | |
runP pExp "x :: (x :: Constraint)" `shouldBe` "SigE (VarE x) (SigT (VarT x) ConstraintT)" | |
runP pExp "x :: (x :: Constrainty)" `shouldBe` "SigE (VarE x) (SigT (VarT x) (ConT Constrainty))" | |
runP pExp "x :: (x :: *)" `shouldBe` "SigE (VarE x) (SigT (VarT x) StarT)" | |
runP pExp "x :: (x ::* )" `shouldBe` "SigE (VarE x) (SigT (VarT x) StarT)" | |
it "TupleT" $ do | |
runPp pExp `sid` "(a, b) :: (Int, String)" | |
runPp pExp `sid` "(# a, b #) :: (# , #) Int String" | |
runP pExp "(#a,b#)" `shouldBe` "UnboxedTupE [VarE a,VarE b]" | |
it "LitT" $ do | |
runPp pType `sid` show "1" | |
runPp pExp `sid` "A :: 3" | |
it "sections" $ do | |
runPp pExp "(`f` x) y" `shouldBe` "((`f` x)) y" | |
runPp pExp "(x `f`) y" `shouldBe` "((x `f`)) y" | |
runPp pExp "(x `f` y) y" `shouldBe` "(x `f` y) y" | |
it "ArithSeqE" $ do | |
runPp pExp `sid` "[a..m] :: [Int]" | |
runPp pExp "[a..] :: [] Int" | |
`shouldBe` "[a..] :: [Int]" | |
runPp pExp `sid` "[b,a..n]" | |
runPp pExp `sid` "[c,c..]" | |
runPp pExp `sid` "[A.c,A.B.c..]" | |
it "LitE" $ do | |
runPp pExp `sid` show "any random string" | |
runPp pExp "12.3" `shouldBe` "3462142213541069 / 281474976710656" | |
runPp pExp `sid` "12" | |
it "qual" $ do | |
runPp pExp `sid` "A.x b" | |
runPp pExp `sid` "A.x . y b" | |
runPp pExp "A.x.y b" `shouldBe` "A.x . y b" | |
it "IfE" $ do | |
runPp pExp `sid` "if a then b else c" | |
it "MultiIfE" $ do | |
runPp pExp "if | a -> b | c -> d" `shouldBe` "if | a -> b\n | c -> d" | |
runPp pExp "if | f a -> b | c -> d" | |
`shouldBe` "if | f a -> b\n | c -> d" | |
runPp pExp "if | Just 1 <- f, f a -> b | c -> d" | |
`shouldBe` "if | Just 1 <- f,\n f a\n -> b\n | c -> d" | |
it "LamE" $ do | |
runPp pExp `sid` "\\x (Just y) -> y" | |
it "RecConE" $ do | |
runP pExp "C { z = z g}" `shouldBe` "RecConE C [(z,AppE (VarE z) (VarE g))]" | |
runP pExp "c { z = z g}" `shouldBe` "RecUpdE (VarE c) [(z,AppE (VarE z) (VarE g))]" | |
runP pExp "f c { z = z g}" `shouldBe` "AppE (VarE f) (RecUpdE (VarE c) [(z,AppE (VarE z) (VarE g))])" | |
runP pExp "C { z = z g }" `shouldBe` "RecConE C [(z,AppE (VarE z) (VarE g))]" | |
it "SigE" $ do | |
runP pSigE "A::B" `shouldBe` "SigE (ConE A) (ConT B)" | |
it "ParensE / TupE" $ do | |
runPp pExp `sid` "a (b c)" | |
runPp pExp `sid` "a (b, c)" | |
it "Type" $ do | |
runPp pType `sid` "A -> B -> C" | |
runPp pType `sid` "A a x y -> B -> C" | |
runPp pType "a ': b" `shouldBe` "(':) a b" | |
runPp pType "a ': b ': c" `shouldBe` "(':) a ((':) b c)" | |
runPp pType "a ': b ': c ': '[]" `shouldBe` "(':) a ((':) b ((':) c '[]))" | |
runPp pType `sid` "(A -> B) -> C" | |
runPp pType `sid` "'(,,) -> C" | |
it "ValD" $ do | |
runPp pDec "Just x = b where b = c" `shouldBe` | |
"Just x = b\n where b = c" | |
runPp pDec `sid` "(a, b, Just c) = a" | |
runP pDec "a@(a, b, Just c) = a" `shouldBe` "ValD (AsP a (TupP [VarP a,VarP b,ConP Just [VarP c]])) (NormalB (VarE a)) []" | |
runP pDec "a@(!a, ~b, Just c) = a" `shouldBe` | |
"ValD (AsP a (TupP [BangP (VarP a),TildeP (VarP b),ConP Just [VarP c]])) (NormalB (VarE a)) []" | |
runP pDec "[a,b,c] = a" `shouldBe` | |
"ValD (ListP [VarP a,VarP b,VarP c]) (NormalB (VarE a)) []" | |
it "RecP/ViewP" $ do | |
runP pPat "R { a = (f -> Just 3) }" `shouldBe` | |
"RecP R [(a,ViewP (VarE f) (ConP Just [LitP (IntegerL 3)]))]" | |
it "UInfixP" $ do | |
runP pPat "f `HCons` x" `shouldBe` "UInfixP (VarP f) HCons (VarP x)" | |
it "FunD" $ do | |
runP pDec "f (Just 1) = 4 where _ = 2" `shouldBe` | |
"FunD f [Clause [ParensP (ConP Just [LitP (IntegerL 1)])] (NormalB (LitE (IntegerL 4))) [ValD WildP (NormalB (LitE (IntegerL 2))) []]]" | |
runP pDecs "f x = x\nf x = x" `shouldBe` | |
"[FunD f [Clause [VarP x] (NormalB (VarE x)) [],Clause [VarP x] (NormalB (VarE x)) []]]" | |
failing = hspec $ do | |
let sid f str = f str `shouldBe` str | |
it "infix decs" $ | |
runP pDecs "x `f` y = (x,y)" `shouldBe` | |
"[FunD f [Clause [VarP x,VarP y] (NormalB (TupE [VarE x, VarE y])) []]]" | |
it "DataD" $ do | |
runP pDecs `sid` "data X = X | Y | Z W" | |
runP pDecs `sid` "data X a = X a | Y | Z W" | |
runP pDecs `sid` "data a ::: b = X a" | |
runP pDecs `sid` "data a ::: b = a :+: b" | |
runP pDecs `sid` "data X = X { a = T }" | |
runP pDecs `sid` "newtype X = X { a = T }" | |
type HP t = S -> Parser t | |
newtype S = S (forall r. (HParser -> S -> r) -> r) | |
data HParser = HParser | |
{pType :: HP Type | |
,pDecs :: HP [Dec] | |
,pExp :: HP Exp | |
,pPat :: HP Pat | |
,pPat' :: HP Pat | |
,pSigP, pUInfixP | |
,pLitP, pVarP, pParensP, pUnboxedTupP, pConP, pAsP, pTildeP, pBangP, pWildP, pListP, pRecP, pViewP :: HP Pat | |
,pDec :: HP Dec | |
-- helpers for dec | |
,pValD :: HP Dec | |
,pFunD :: HP Dec | |
,pBody :: HP Body | |
,pWhere :: HP [Dec] | |
,pSigD :: HP Dec | |
,pType' :: HP Type | |
-- ^ a subset of Type that must consume | |
-- input to produce a constructor (no leading spaces) | |
,pExp' :: HP Exp | |
-- ^ a subset of Exp that must consume | |
-- input to produce a constructor (no leading spaces) | |
,pVarE -- ^ @{ x }@ | |
,pConE -- ^ @data T1 = C1 t1 t2; p = {C1} e1 e2 @ | |
,pLitE -- ^ @{ 5 or 'c'}@ | |
,pSpaceSep {- ^ AppE @{ f x }@ | |
RecConE @{ T { x = y, z = w } }@ | |
RecUpdE @{ (f x) { z = w } }@ | |
UInfixE @{x + y}@ | |
using classifySpaceSep | |
-} | |
,pInfixE -- ^ @{x + y} or {(x+)} or {(+ x)} or {(+)}@ | |
,pLamE -- ^ @{ \ p1 p2 -> e }@ | |
,pLamCaseE -- ^ @{ \case m1; m2 }@ | |
,pTupE -- ^ @{ (e1,e2) } @ | |
-- or ParensE @{ (e) }@ | |
,pUnboxedTupE -- @{ (# e1,e2 #) } @ | |
,pCondE -- ^ @{ if e1 then e2 else e3 }@ | |
,pMultiIfE -- ^ @{ if | g1 -> e1 | g2 -> e2 }@ | |
,pLetE -- ^ @{ let x=e1; y=e2 in e3 }@ | |
,pCaseE -- ^ @{ case e of m1; m2 }@ | |
,pDoE -- ^ @{ do { p <- e1; e2 } }@ | |
,pCompE -- ^ @{ [ (x,y) | x <- xs, y <- ys ] }@ | |
,pArithSeqE -- ^ @{ [ 1 ,2 .. 10 ] }@ | |
,pListE -- ^ @{ [1,2,3] }@ | |
,pSigE -- ^ @{ e :: t }@ | |
:: HP Exp | |
-- helpers for Exp | |
,pMatch :: HP Match | |
,pGuard :: HP Guard | |
,pStmt :: HP Stmt | |
,pFieldExps :: HP [FieldExp] | |
,pForallT -- ^ @forall \<vars\>. \<ctxt\> -> \<type\>@ | |
,pSpaceSepT | |
,pSigT -- ^ @t :: k@ | |
,pVarT -- ^ @a@ | |
,pConT -- ^ @T@ or @Constraint@ | |
,pPromotedT -- ^ @'T@ | |
,pTupleT -- ^ @(,), (,,), etc.@ or @(#,#), (#,,#), etc.@ | |
,pArrowT -- ^ @->@ | |
,pListT -- ^ @[]@ | |
,pPromotedTupleT -- ^ @'(), '(,), '(,,), etc.@ | |
,pStarT -- ^ @*@ | |
,pLitT :: HP Type -- ^ @0,1,2, etc.@ | |
-- helpers for Type | |
,pKind :: HP Kind | |
,pCxt :: HP Cxt | |
,pPred :: HP Pred | |
,pLit :: HP Lit | |
,pQual :: (HParser -> HP Name) -> HP Name | |
,pVar | |
,pVarAlpha | |
,pVarSym | |
,pCon :: HP Name | |
,pConSym :: HP String | |
,pConAlpha :: HP String | |
,pAlpha :: HP Char | |
,pSy :: HP Char | |
,pCharLit :: HP Char | |
,pTyVarBndr :: HP TyVarBndr | |
,pForall :: HP () -- ^ @forall@ | |
,pLam :: HP () -- ^ @\\@ | |
,pRArrow :: HP () -- ^ @->@ | |
,pLArrow :: HP () -- ^ @<-@ | |
,pDColon :: HP () -- ^ @::@ | |
,pDotDot :: HP () -- ^ @..@ | |
-- layout-related | |
,pSemi :: HP () | |
,pLBrace :: HP () | |
,pRBrace :: HP () | |
,isSy :: S -> Char -> Bool | |
,classifySpaceSep :: S -> [Either Exp [FieldExp]] -> Exp | |
,classifySpaceSepT :: S -> [Type] -> Type | |
} | |
fixP :: (HParser -> HP t) -> HParser -> Parser t | |
fixP getP p = getP p s | |
where s :: S | |
s = S $ \f -> f p s | |
{- Fields of ‘HParser’ not initialised: pSigD | |
-} | |
instance Default HParser where | |
def = HParser | |
{pType = \(S s) -> (pSpaces *> s pSpaceSepT <* pSpaces) | |
<|> s pSigT | |
,pType' = \(S s) -> micro (s pConT) 1 -- so a::* is SigT _ StarT | |
<|> s pStarT | |
<|> s pForallT | |
<|> s pLitT | |
<|> s pVarT | |
<|> s pPromotedT | |
<|> s pTupleT | |
<|> s pArrowT | |
<|> s pListT | |
<|> s pPromotedTupleT | |
,pExp = \(S s) -> (pSpaces *> s pSpaceSep <* pSpaces) | |
<|> s pSigE | |
,pExp' = \(S s) -> s pVarE | |
<|> s pConE | |
<|> s pLitE | |
<|> s pInfixE | |
<|> s pTupE | |
<|> s pLamE | |
<|> s pLamCaseE | |
<|> s pUnboxedTupE | |
<|> s pCondE | |
<|> s pMultiIfE | |
<|> s pLetE | |
<|> s pCaseE | |
<|> s pDoE | |
<|> s pCompE | |
<|> s pArithSeqE | |
<|> s pListE | |
,pPat = \(S s) -> pSpaces *> (s pPat' <|> s pSigP <|> s pUInfixP) <* pSpaces | |
,pSigP = \(S s) -> SigP <$> s pPat' <*> (s pDColon *> s pType) | |
,pUInfixP = \(S s) -> | |
let infixCon = pSym '`' *> (toName [] <$> s pConAlpha) <* pSym '`' | |
in UInfixP <$> s pPat' <*> infixCon <*> s pPat | |
,pPat' = \(S s) -> s pLitP | |
<|> s pVarP | |
<|> s pParensP | |
<|> s pUnboxedTupP | |
<|> s pConP | |
<|> s pAsP | |
<|> s pTildeP | |
<|> s pBangP | |
<|> s pWildP | |
<|> s pListP | |
<|> s pRecP | |
<|> s pViewP | |
,pLitP = \(S s) -> LitP <$> s pLit | |
,pVarP = \(S s) -> VarP <$> s pVarAlpha | |
,pParensP = \(S s) -> | |
let tupP [x] = ParensP x | |
tupP xs = TupP xs | |
in pParens $ tupP <$> pList1Sep pComma (s pPat) | |
,pUnboxedTupP = \(S s) -> | |
pToken "(#" *> (UnboxedTupP <$> ((:) <$> s pPat <*> (pList1 (pComma *> s pPat)))) | |
<* pToken "#)" | |
,pConP = \(S s) -> | |
ConP <$> s pCon <*> pListSep pSpaces (s pPat) | |
,pAsP = \(S s) -> AsP <$> (s pVarAlpha <* pToken "@") <*> s pPat | |
,pTildeP = \(S s) -> TildeP <$> (pToken "~" *> s pPat) | |
,pBangP = \(S s) -> BangP <$> (pToken "!" *> s pPat) | |
,pWildP = \(S s) -> WildP <$ pToken "_" | |
,pListP = \(S s) -> ListP <$> listParser (s pPat) | |
,pRecP = \(S s) -> | |
let con = toName [] <$> s pConAlpha <* pSpaces | |
fieldPats = pListSep pComma fieldPat | |
fieldPat = (,) <$> s pVarAlpha <*> (pToken "=" *> s pPat) | |
in RecP <$> con <*> pBraces fieldPats | |
,pViewP = \(S s) -> pParens $ ViewP <$> s pExp <*> (s pRArrow *> s pPat) | |
,pVar = \(S s) -> micro (s pVarAlpha <|> s pVarSym) 1 | |
-- micro adds a penalty to choosing a variable name, | |
-- so if/then/else are consumed by pToken, A.B.c does not | |
-- use . as variable | |
,pVarE = \(S s) -> VarE <$> s (optQual pVar) | |
<?> "VarE" | |
,pConE = \(S s) -> ConE <$> s (optQual pCon) | |
<?> "ConE" | |
,pLitE = \(S s) -> LitE <$> s pLit | |
,pSpaceSepT = \(S s) -> s classifySpaceSepT <$> | |
pList1Sep_ng pSpaces (s pType') | |
,pSpaceSep = \ (S s) -> | |
let e = Left <$> s pExp' <|> Right <$> s pFieldExps | |
in s classifySpaceSep <$> pList1Sep_ng pSpaces e | |
,classifySpaceSepT = \ (S s) xs -> | |
let isOp (VarT (Name (OccName (n:_)) _)) = s isSy n | |
isOp (ConT (Name (OccName (':':_)) _)) = True | |
isOp ArrowT = True | |
isOp PromotedConsT = True | |
isOp _ = False | |
-- this is not really as general as it could be | |
toCxt (AppT (ConT n) t) = [ClassP n (unapp t)] | |
toCxt (ConT n) = [ClassP n []] | |
toCxt (VarT n) = [ClassP n []] | |
toCxt (VarT (Name (OccName "~") NameS) `AppT` x `AppT` y) = [EqualP x y] | |
toCxt a = concatMap toCxt (reverse (fromTuple 0 a)) | |
fromTuple n (AppT a b) = b : fromTuple (n+1) a | |
fromTuple n (TupleT m) | |
| n == m = [] | |
| otherwise = error "QQParse.fromTuple" | |
fromTuple _ x = error ("QQParse.fromTuple: don't know how to handle " ++ show x) | |
unapp (AppT a b) = a : unapp b | |
unapp x = [x] | |
infixF (Right a : Left (VarT (Name (OccName "=>") _)) : cs) = ForallT [] (toCxt a) (infixF cs) | |
infixF (Right a : Left b : cs) = infixF (Right (AppT b a) : cs) | |
infixF (Left a : b : cs) = AppT a (infixF (b:cs)) | |
infixF (Right a : b : cs) = AppT a (infixF (b:cs)) | |
infixF [a] = either id id a | |
in infixF $ | |
mapMaybe (\x -> case x of | |
[a] | isOp a -> Just (Left a) | |
(a:b) -> Just (Right (foldl AppT a b)) | |
_ -> Nothing) | |
$ split (whenElt isOp) xs | |
,classifySpaceSep = \ (S s) xs -> | |
let isOp (VarE (Name (OccName (n:_)) _)) = s isSy n | |
isOp (ConE (Name (OccName (':':_)) _)) = True | |
isOp (InfixE Nothing v Nothing) = not (isOp v) | |
isOp _ = False | |
minfix (Right a : Left op : Right b : xs) | |
| InfixE Nothing op' Nothing <- op = minfix (Right (UInfixE a op' b) : xs) | |
| otherwise = minfix (Right (UInfixE a op b) : xs) | |
minfix (Right y : Left (InfixE Nothing x Nothing) : xs) = minfix (Left (InfixE (Just y) x Nothing) : xs) | |
minfix (Left (InfixE Nothing x Nothing) : Right y : xs) = minfix (Left (InfixE Nothing x (Just y)) : xs) | |
minfix (Left x : xs) = x : minfix xs | |
minfix (Right x : xs) = x : minfix xs | |
minfix [] = [] | |
applyFieldExps (Left (ConE n) : Right fexp : as) = applyFieldExps (Left (RecConE n fexp) : as) | |
applyFieldExps (Left e : Right fexp : as) = applyFieldExps (Left (RecUpdE e fexp) : as) | |
applyFieldExps (Left e : es) = e : applyFieldExps es | |
applyFieldExps [] = [] | |
in foldl1 AppE $ minfix | |
$ mapMaybe (\x -> case x of | |
[a] | isOp a -> Just (Left a) | |
a:b -> Just (Right (foldl AppE a b)) | |
_ -> Nothing | |
) | |
$ split (whenElt isOp) | |
$ applyFieldExps xs | |
,pInfixE = \(S s) -> | |
InfixE Nothing <$> (pSym '`' *> (VarE <$> s pVarAlpha | |
<|> s pConE) <* pSym '`') | |
<*> pure Nothing | |
,pLamE = \(S s) -> LamE <$> (pSym '\\' *> some (s pPat) <* s pRArrow) <*> s pExp | |
,pLamCaseE = \(S s) -> LamCaseE <$> | |
(s pLam *> pToken "case" *> pList1Sep (s pSemi) (s pMatch)) | |
,pTupE = \(S s) -> (\x -> case x of | |
[e] -> ParensE e | |
_ -> TupE x) | |
<$> (pLParen *> (pList1Sep pComma (s pExp)) <* pSym ')') | |
,pUnboxedTupE = \(S s) -> pToken "(#" *> | |
(UnboxedTupE <$> pList1Sep pComma (s pExp)) | |
<* pSymbol "#)" | |
,pCondE = \(S s) -> CondE <$> | |
(pSymbol "if" *> s pExp) | |
<*> (pSymbol "then" *> s pExp) | |
<*> (pSymbol "else" *> s pExp) | |
,pMultiIfE = \(S s) -> | |
let guardE = (,) <$> s pGuard | |
<*> (s pRArrow *> s pExp) | |
in MultiIfE <$> (pSymbol "if" *> pSome guardE) -- ^ @{ if | g1 -> e1 | g2 -> e2 }@ | |
,pGuard = \(S s) -> | |
let patG [NoBindS e] = NormalG e | |
patG stmts = PatG stmts | |
in pToken "|" *> ( patG <$> pList1Sep pComma (s pStmt) ) | |
,pMatch = \(S s) -> | |
let normalB = s pRArrow *> (NormalB <$> s pExp) | |
guardedB = GuardedB <$> pList1 ((,) <$> s pGuard <*> (s pRArrow *> s pExp)) | |
in Match <$> s pPat <*> (normalB <|> guardedB) <*> s pWhere | |
,pLetE = \(S s) -> | |
let decs = pToken "let" *> s pLBrace *> s pDecs <* s pRBrace | |
in LetE <$> decs <*> (pToken "in" *> s pExp) | |
,pCaseE = \(S s) -> CaseE <$> (pToken "case" *> s pExp) | |
<*> some (s pMatch) -- ^ @{ case e of m1; m2 }@ | |
,pDoE = \(S s) -> | |
let stmts = pToken "do" *> s pLBrace *> pList1Sep (s pSemi) (s pStmt) | |
<* s pRBrace | |
in DoE <$> stmts | |
{- | |
| CompE [Stmt] -- ^ @{ [ (x,y) | x <- xs, y <- ys ] }@ | |
-- | |
-- The result expression of the comprehension is | |
-- the /last/ of the @'Stmt'@s, and should be a 'NoBindS'. | |
-- | |
-- E.g. translation: | |
-- | |
-- > [ f x | x <- xs ] | |
-- | |
-- > CompE [BindS (VarP x) (VarE xs), NoBindS (AppE (VarE f) (VarE x))] | |
| ArithSeqE Range -- ^ @{ [ 1 ,2 .. 10 ] }@ | |
,pArithSeqE -- ^ @{ [ 1 ,2 .. 10 ] }@ | |
-} | |
,pArithSeqE = \(S s) -> | |
let toRange a (Just b) (Just c) = FromThenToR a b c | |
toRange a Nothing Nothing = FromR a | |
toRange a (Just b) Nothing = FromThenR a b | |
toRange a Nothing (Just c) = FromToR a c | |
addCon a b c = ArithSeqE (toRange a b c) | |
in addCon <$> (pSym '[' *> s pExp) | |
<*> optional (pComma *> s pExp) | |
<*> (s pDotDot *> optional (s pExp) <* pSpaces) | |
<* pSym ']' | |
,pCompE = \(S s) -> | |
let toComp e stmts = CompE (stmts ++ [NoBindS e]) | |
in toComp <$> (pSym '[' *> s pExp) | |
<*> (pSym '|' *> pList1Sep pComma (s pStmt) <* pSym ']') | |
-- ^ @{ [ (x,y) | x <- xs, y <- ys ] }@ | |
,pFieldExps = \(S s) -> pBraces (pList1Sep_ng pComma | |
((,) <$> (s pVarAlpha <* pToken "=") | |
<*> s pExp)) | |
,pListE = \(S s) -> ListE <$> listParser (s pExp) | |
,pSigE = \(S s) -> SigE <$> s pExp' <*> (pSpaces *> s pDColon *> s pType) | |
,pDecs = \(S s) -> | |
let mergeFunDs (FunD x a : FunD y b : xs) | x == y = mergeFunDs (FunD x (a ++ b) : xs) | |
mergeFunDs (x : xs) = x : mergeFunDs xs | |
mergeFunDs [] = [] | |
in mergeFunDs <$> pList1 (s pDec) | |
,pDec = \(S s) -> s pValD | |
<|> s pFunD | |
,pValD = \(S s) -> | |
ValD <$> s pPat <*> s pBody <*> s pWhere | |
,pWhere = \(S s) -> pToken "where" *> s pDecs <|> pure [] | |
,pBody = \(S s) -> | |
let normalB = NormalB <$> (pToken "=" *> s pExp) | |
guardedB = GuardedB <$> | |
pList1 ((,) <$> (pToken "|" *> s pGuard) | |
<*> (pToken "=" *> s pExp)) | |
in normalB <|> guardedB | |
,pFunD = \(S s) -> | |
let pClause = Clause <$> pList1 (s pPat) <*> s pBody <*> s pWhere | |
in FunD <$> s pVarAlpha <* pSpaces <*> ((:[]) <$> pClause) | |
,pStmt = \(S s) -> | |
NoBindS <$> s pExp <|> | |
BindS <$> s pPat <*> (s pLArrow *> s pExp) | |
<|> LetS <$> (pToken "let" *> s pDecs) | |
-- ParS XXX | |
,pSigT = \(S s) -> SigT <$> s pType' <*> (pSpaces *> s pDColon *> s pKind) | |
,pVarT = \(S s) -> VarT <$> (s pVarAlpha <|> micro (s pVarSym) 2) | |
,pConT = \(S s) -> | |
let f (Name (OccName "Constraint") _) = ConstraintT | |
f x = ConT x | |
in f <$> s pCon | |
,pForallT = \(S s) -> | |
let tyVarBndrs = s pForall *> pSpaces *> some (s pTyVarBndr) <* pSymbol "." | |
in ForallT | |
<$> tyVarBndrs | |
<*> (s pCxt <* pToken "=>") | |
<*> s pType | |
,pTupleT = \ (S s) -> | |
let appliedTupT c = f <$> pList1Sep pComma (s pType) | |
where f [x] = x | |
f xs = foldl AppT (c (length xs)) xs | |
unappliedTupT c = c . (+1) . length <$> some pComma | |
tt con = appliedTupT con <|> unappliedTupT con | |
in pSymbol "(#" *> tt UnboxedTupleT <* pSymbol "#)" | |
<|> pParens (tt TupleT) | |
,pArrowT = \ (S s) -> ArrowT <$ s pRArrow | |
,pLitT = \ _ -> fmap LitT $ | |
NumTyLit <$> pInteger | |
<|> StrTyLit <$> pQuotedString | |
,pPromotedT = \ (S s) -> | |
let alph = s (optQual $ \_ _ -> s pCon <|> s pVarAlpha) | |
sym = pParens (s (optQual pVarSym)) | |
alphSym = alph <|> sym | |
promotedT (Name (OccName ":") _) = PromotedConsT | |
promotedT x = PromotedT x | |
in pSym '\'' *> (promotedT <$> alphSym) | |
,pPromotedTupleT = \ (S s) -> | |
let pList2Sep_ng sep p = (:) <$> p <*> (sep *> pList1Sep_ng sep p) | |
notApp = PromotedTupleT . (+1) . length <$> some pComma | |
app = f <$> pList2Sep_ng pComma (s pType) | |
f xs = foldl AppT (PromotedTupleT (length xs)) xs | |
in pSym '\'' *> pParens (notApp <|> app) | |
,pStarT = \_ -> StarT <$ pSymbol "*" | |
,pListT = \(S s) -> | |
let f Nothing [] = ListT | |
f Nothing [x] = ListT `AppT` x | |
f _ xs = foldr (\a b -> PromotedConsT `AppT` a `AppT` b) PromotedNilT xs | |
in f <$> optional (pSym '\'') <*> pBrackets (pListSep pComma (s pType)) | |
,pCxt = \(S s) -> (:[]) <$> s pPred <|> pParens (some (s pPred)) | |
,pPred = \(S s) -> ClassP <$> (s pCon <* pSpaces) <*> many (s pType) | |
<|> EqualP <$> s pType' <*> (pSymbol "~" *> s pType) | |
,pKind = \(S s) -> s pType | |
,pTyVarBndr = \(S s) -> (PlainTV <$> s pVarAlpha) <|> | |
pParens (KindedTV <$> s pVarAlpha <*> (s pDColon *> s pKind)) | |
,pQual = \next (S s) -> addQual | |
<$> (pList1 (s pConAlpha <* pSym '.') <?> "module name") | |
<*> s next | |
,pLit = \ (S s) -> | |
let toLit x | Just n <- readMaybe x = IntegerL n | |
| otherwise = RationalL (toRational (read x :: Double)) | |
in | |
StringL <$> pQuotedString | |
<|> CharL <$> s pCharLit | |
<|> toLit <$> micro pDoubleStr 1 | |
{- | |
<|> IntPrimL <$> pInteger | |
<|> WordPrimL <$> pInteger | |
| FloatPrimL Rational | |
| DoublePrimL Rational | |
| StringPrimL [GHC.Word.Word8] | |
-} | |
,pCharLit = \ _ -> pSym '\'' <* | |
(pSatisfy (/= '\\') (Insertion "\\" '\\' 100) | |
<<|> ('\'' <$ pToken "\\'")) | |
<* pSym '\'' | |
,pVarSym = \(S s) -> | |
-- let illegalSym = (s pDColon <|> s pDotDot <|> s pRArrow <|> s pLArrow) *> pFail | |
-- in illegalSym <<|> | |
toName [] <$> pSome (s pSy) | |
,pVarAlpha = \ (S s) -> toName [] <$> ((:) <$> pLower <*> many (s pAlpha)) | |
<* pSpaces | |
,pCon = \ (S s) -> toName [] <$> (s pConAlpha <|> s pConSym) | |
<* pSpaces | |
,pConAlpha = \ (S s) -> (:) <$> pUpper <*> pList (s pAlpha) | |
,pConSym = \ (S s) -> | |
let sndSy = (:) <$> s pSy <*> pList (s pSy <|> pSym ':') | |
sndColon = (:) <$> pSym ':' <*> pList1 (s pSy <|> pSym ':') | |
in (:) <$> pSym ':' <*> (sndSy <|> sndColon <<|> pure []) | |
,pAlpha = \ _s -> pSatisfy (\x -> isAlpha x || x `elem` "'_") (Insertion "a" 'a' 100) | |
,pSy = \ (S s) -> pSatisfy (s isSy) (Insertion "+" '+' 100) | |
,isSy = \ _ x -> (isSymbol x || x `elem` "-!#$%^&*.?<>~") && x /= '`' | |
,pLam = \ _ -> () <$ pToken "\\" | |
,pRArrow = \ _ -> () <$ pToken "->" | |
,pLArrow = \ _ -> () <$ pToken "<-" | |
,pDColon = \ _ -> () <$ pToken "::" | |
,pForall = \ _ -> () <$ pToken "forall" | |
,pDotDot = \_ -> () <$ pToken ".." | |
,pSemi = \ _ -> () <$ pSym ';' -- or do layout here? | |
,pLBrace = \_ -> () <$ pSym '{' | |
,pRBrace = \_ -> () <$ pSym '}' | |
} | |
-- | s (qual pVar) | |
qual :: (HParser -> HP Name) -> t -> HP Name | |
qual x _ (S s) = s $ \hp _ -> pQual hp x (S s) | |
optQual :: (HParser -> HP Name) -> t -> HP Name | |
optQual x _ (S s) = s $ \hp _ -> pQual hp x (S s) <|> x hp (S s) | |
toName :: [String] -> String -> Name | |
toName [] var = Name (OccName var) NameS | |
toName mn var = Name (OccName var) (NameQ (ModName (intercalate "." mn))) | |
addQual :: [String] -> Name -> Name | |
addQual ms (Name (OccName var) NameS) = toName ms var |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment