{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} -- we have lots of parsers which don't want signatures; and we have -- uniplate patterns {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-incomplete-patterns -fno-warn-name-shadowing #-} {-# OPTIONS_HADDOCK show-extensions #-} -- | -- Module : Yi.Syntax.Haskell -- License : GPL-2 -- Maintainer : yi-devel@googlegroups.com -- Stability : experimental -- Portability : portable -- -- NOTES: -- Note if the layout of the first line (not comments) -- is wrong the parser will only parse what is in the blocks given by Layout.hs module Yi.Syntax.Haskell ( PModule , PModuleDecl , PImport , Exp (..) , Tree , parse , indentScanner ) where import Control.Applicative import Data.Foldable hiding (elem, notElem) import Data.Maybe import Data.List ((\\)) import Yi.IncrementalParse import Yi.Lexer.Alex import Yi.Lexer.Haskell import Yi.Syntax.Layout import Yi.Syntax.Tree import Yi.Syntax import Control.Arrow ((&&&)) indentScanner :: Scanner (AlexState lexState) TT -> Scanner (Yi.Syntax.Layout.State Token lexState) TT indentScanner = layoutHandler startsLayout [(Special '(', Special ')'), (Reserved Let, Reserved In), (Special '[', Special ']'), (Special '{', Special '}')] ignoredToken (Special '<', Special '>', Special '.') isBrace -- HACK: We insert the Special '<', '>', '.', which do not occur in -- normal haskell parsing. -- | Check if a token is a brace, this function is used to -- fix the layout so that do { works correctly isBrace :: TT -> Bool isBrace (Tok br _ _) = Special '{' == br -- | Theese are the tokens ignored by the layout handler. ignoredToken :: TT -> Bool ignoredToken (Tok t _ (Posn{})) = isComment t || t == CppDirective type Tree = PModule type PAtom = Exp type Block = Exp type PGuard = Exp type PModule = Exp type PModuleDecl = Exp type PImport = Exp -- | Exp can be expression or declaration data Exp t = PModule { comments :: [t] , progMod :: Maybe (PModule t) } | ProgMod { modDecl :: PModuleDecl t , body :: PModule t -- ^ The module declaration part } | Body { imports :: Exp t -- [PImport t] , content :: Block t , extraContent :: Block t -- ^ The body of the module } | PModuleDecl { moduleKeyword :: PAtom t , name :: PAtom t , exports :: Exp t , whereKeyword :: Exp t } | PImport { importKeyword :: PAtom t , qual :: Exp t , name' :: PAtom t , as :: Exp t , specification :: Exp t } | TS t [Exp t] -- ^ Type signature | PType { typeKeyword :: PAtom t , typeCons :: Exp t , equal :: PAtom t , btype :: Exp t } -- ^ Type declaration | PData { dataKeyword :: PAtom t , dtypeCons :: Exp t , dEqual :: Exp t , dataRhs :: Exp t } -- ^ Data declaration | PData' { dEqual :: PAtom t , dataCons :: Exp t -- ^ Data declaration RHS } | PClass { cKeyword :: PAtom t -- Can be class or instance , cHead :: Exp t , cwhere :: Exp t -- ^ Class declaration } -- declaration -- declarations and parts of them follow | Paren (PAtom t) [Exp t] (PAtom t) -- ^ A parenthesized, bracked or braced | Block [Exp t] -- ^ A block of things separated by layout | PAtom t [t] -- ^ An atom is a token followed by many comments | Expr [Exp t] -- ^ | PWhere (PAtom t) (Exp t) (Exp t) -- ^ Where clause | Bin (Exp t) (Exp t) -- an error with comments following so we never color comments in wrong -- color. The error has an extra token, the Special '!' token to -- indicate that it contains an error | PError { errorTok :: t , marker :: t , commentList :: [t] -- ^ An wrapper for errors } -- rhs that begins with Equal | RHS (PAtom t) (Exp t) -- ^ Righthandside of functions with = | Opt (Maybe (Exp t)) -- ^ An optional | Modid t [t] -- ^ Module identifier | Context (Exp t) (Exp t) (PAtom t) -- ^ | PGuard [PGuard t] -- ^ Righthandside of functions with | -- the PAtom in PGuard' does not contain any comments | PGuard' (PAtom t) (Exp t) (PAtom t) -- type constructor is just a wrapper to indicate which highlightning to -- use. | TC (Exp t) -- ^ Type constructor -- data constructor same as with the TC constructor | DC (Exp t) -- ^ Data constructor | PLet (PAtom t) (Exp t) (Exp t) -- ^ let expression | PIn t [Exp t] deriving (Show, Foldable) instance IsTree Exp where emptyNode = Expr [] uniplate tree = case tree of (ProgMod a b) -> ([a,b], \[a,b] -> ProgMod a b) (Body x exp exp') -> ([x, exp, exp'], \[x, exp, exp'] -> Body x exp exp') (PModule x (Just e)) -> ([e],\[e] -> PModule x (Just e)) (Paren l g r) -> -- TODO: improve (l:g ++ [r], \(l:gr) -> Paren l (init gr) (last gr)) (RHS l g) -> ([l,g],\[l,g] -> (RHS l g)) (Block s) -> (s,Block) (PLet l s i) -> ([l,s,i],\[l,s,i] -> PLet l s i) (PIn x ts) -> (ts,PIn x) (Expr a) -> (a,Expr) (PClass a b c) -> ([a,b,c],\[a,b,c] -> PClass a b c) (PWhere a b c) -> ([a,b,c],\[a,b,c] -> PWhere a b c) (Opt (Just x)) -> ([x],\[x] -> (Opt (Just x))) (Bin a b) -> ([a,b],\[a,b] -> (Bin a b)) (PType a b c d) -> ([a,b,c,d],\[a,b,c,d] -> PType a b c d) (PData a b c d) -> ([a,b,c,d],\[a,b,c,d] -> PData a b c d) (PData' a b) -> ([a,b] ,\[a,b] -> PData' a b) (Context a b c) -> ([a,b,c],\[a,b,c] -> Context a b c) (PGuard xs) -> (xs,PGuard) (PGuard' a b c) -> ([a,b,c],\[a,b,c] -> PGuard' a b c) (TC e) -> ([e],\[e] -> TC e) (DC e) -> ([e],\[e] -> DC e) PModuleDecl a b c d -> ([a,b,c,d],\[a,b,c,d] -> PModuleDecl a b c d) PImport a b c d e -> ([a,b,c,d,e],\[a,b,c,d,e] -> PImport a b c d e) t -> ([],const t) -- | The parser parse :: P TT (Tree TT) parse = pModule <* eof -- | @pModule@ parse a module pModule :: Parser TT (PModule TT) pModule = PModule <$> pComments <*> optional (pBlockOf' (ProgMod <$> pModuleDecl <*> pModBody <|> pBody)) -- | Parse a body that follows a module pModBody :: Parser TT (PModule TT) pModBody = (exact [startBlock] *> (Body <$> pImports <*> ((pTestTok elems *> pBod) <|> pEmptyBL) <* exact [endBlock] <*> pBod <|> Body <$> noImports <*> ((pBod <|> pEmptyBL) <* exact [endBlock]) <*> pBod)) <|> (exact [nextLine] *> pBody) <|> Body <$> pure emptyNode <*> pEmptyBL <*> pEmptyBL where pBod = Block <$> pBlocks pTopDecl elems = [Special ';', nextLine, startBlock] -- | @pEmptyBL@ A parser returning an empty block pEmptyBL :: Parser TT (Exp TT) pEmptyBL = Block <$> pEmpty -- | Parse a body of a program pBody :: Parser TT (PModule TT) pBody = Body <$> noImports <*> (Block <$> pBlocks pTopDecl) <*> pEmptyBL <|> Body <$> pImports <*> ((pTestTok elems *> (Block <$> pBlocks pTopDecl)) <|> pEmptyBL) <*> pEmptyBL where elems = [nextLine, startBlock] noImports :: Parser TT (Exp TT) noImports = notNext [Reserved Import] *> pure emptyNode where notNext f = testNext $ uncurry (||) . (&&&) isNothing (flip notElem f . tokT . fromJust) -- Helper functions for parsing follows -- | Parse Variables pVarId :: Parser TT (Exp TT) pVarId = pAtom [VarIdent, Reserved Other, Reserved As] -- | Parse VarIdent and ConsIdent pQvarid :: Parser TT (Exp TT) pQvarid = pAtom [VarIdent, ConsIdent, Reserved Other, Reserved As] -- | Parse an operator using please pQvarsym :: Parser TT (Exp TT) pQvarsym = pParen ((:) <$> please (PAtom <$> sym isOperator <*> pComments) <*> pEmpty) -- | Parse any operator isOperator :: Token -> Bool isOperator (Operator _) = True isOperator (ReservedOp _) = True isOperator (ConsOperator _) = True isOperator _ = False -- | Parse a consident pQtycon :: Parser TT (Exp TT) pQtycon = pAtom [ConsIdent] -- | Parse many variables pVars :: Parser TT (Exp TT) pVars = pMany pVarId -- | Parse a nextline token (the nexLine token is inserted by Layout.hs) nextLine :: Token nextLine = Special '.' -- | Parse a startBlock token startBlock :: Token startBlock = Special '<' -- | Parse a endBlock token endBlock :: Token endBlock = Special '>' pEmpty :: Applicative f => f [a] pEmpty = pure [] pToList :: Applicative f => f a -> f [a] pToList = (box <$>) where box x = [x] -- | @sym f@ returns a parser parsing @f@ as a special symbol sym :: (Token -> Bool) -> Parser TT TT sym f = symbol (f . tokT) -- | @exact tokList@ parse anything that is in @tokList@ exact :: [Token] -> Parser TT TT exact = sym . flip elem -- | @please p@ returns a parser parsing either @p@ or recovers with the -- (Special '!') token. please :: Parser TT (Exp TT) -> Parser TT (Exp TT) please = (<|>) (PError <$> recoverWith errTok <*> errTok <*> pEmpty) -- | Parse anything, as errors pErr :: Parser TT (Exp TT) pErr = PError <$> recoverWith (sym $ not . uncurry (||) . (&&&) isComment (== CppDirective)) <*> errTok <*> pComments -- | Parse an ConsIdent ppCons :: Parser TT (Exp TT) ppCons = ppAtom [ConsIdent] -- | Parse a keyword pKW :: [Token] -> Parser TT (Exp TT) -> Parser TT (Exp TT) pKW k r = Bin <$> pAtom k <*> r -- | Parse an unary operator with and without using please pOP :: [Token] -> Parser TT (Exp TT) -> Parser TT (Exp TT) pOP op r = Bin <$> pAtom op <*> r --ppOP op r = Bin <$> ppAtom op <*> r -- | Parse comments pComments :: Parser TT [TT] pComments = many $ sym $ uncurry (||) . (&&&) isComment (== CppDirective) -- | Parse something thats optional pOpt :: Parser TT (Exp TT) -> Parser TT (Exp TT) pOpt x = Opt <$> optional x -- | Parse an atom with, and without using please pAtom, ppAtom :: [Token] -> Parser TT (Exp TT) pAtom = flip pCAtom pComments ppAtom at = pAtom at <|> recoverAtom recoverAtom :: Parser TT (Exp TT) recoverAtom = PAtom <$> recoverWith errTok <*> pEmpty -- | Parse an atom with optional comments pCAtom :: [Token] -> Parser TT [TT] -> Parser TT (Exp TT) pCAtom r c = PAtom <$> exact r <*> c pBareAtom a = pCAtom a pEmpty -- | @pSepBy p sep@ parse /zero/ or more occurences of @p@, separated -- by @sep@, with optional ending @sep@, -- this is quite similar to the sepBy function provided in -- Parsec, but this one allows an optional extra separator at the end. -- -- > commaSep p = p `pSepBy` (symbol (==(Special ','))) pSepBy :: Parser TT (Exp TT) -> Parser TT (Exp TT) -> Parser TT [Exp TT] pSepBy p sep = pEmpty <|> (:) <$> p <*> (pSepBy1 p sep <|> pEmpty) <|> pToList sep -- optional ending separator where pSepBy1 r p' = (:) <$> p' <*> (pEmpty <|> pSepBy1 p' r) -- | Separate a list of things separated with comma inside of parenthesis pParenSep :: Parser TT (Exp TT) -> Parser TT (Exp TT) pParenSep = pParen . flip pSepBy pComma -- | Parse a comma separator pComma :: Parser TT (Exp TT) pComma = pAtom [Special ','] -- End of helper functions Parsing different parts follows -- | Parse a Module declaration pModuleDecl :: Parser TT (PModuleDecl TT) pModuleDecl = PModuleDecl <$> pAtom [Reserved Module] <*> ppAtom [ConsIdent] <*> pOpt (pParenSep pExport) <*> (optional (exact [nextLine]) *> (Bin <$> ppAtom [Reserved Where]) <*> pMany pErr) <* pTestTok elems where elems = [nextLine, startBlock, endBlock] pExport :: Parser TT (Exp TT) pExport = optional (exact [nextLine]) *> please ( pVarId <|> pEModule <|> Bin <$> pQvarsym <*> (DC <$> pOpt expSpec) -- typeOperator <|> Bin <$> (TC <$> pQtycon) <*> (DC <$> pOpt expSpec) ) where expSpec = pParen (pToList (please (pAtom [ReservedOp DoubleDot])) <|> pSepBy pQvarid pComma) -- | Check if next token is in given list pTestTok :: [Token] -> Parser TT () pTestTok f = testNext (uncurry (||) . (&&&) isNothing (flip elem f . tokT . fromJust)) -- | Parse several imports pImports :: Parser TT (Exp TT) -- [PImport TT] pImports = Expr <$> many (pImport <* pTestTok pEol <* optional (some $ exact [nextLine, Special ';'])) where pEol = [Special ';', nextLine, endBlock] -- | Parse one import pImport :: Parser TT (PImport TT) pImport = PImport <$> pAtom [Reserved Import] <*> pOpt (pAtom [Reserved Qualified]) <*> ppAtom [ConsIdent] <*> pOpt (pKW [Reserved As] ppCons) <*> (TC <$> pImpSpec) where pImpSpec = Bin <$> pKW [Reserved Hiding] (please pImpS) <*> pMany pErr <|> Bin <$> pImpS <*> pMany pErr <|> pMany pErr pImpS = DC <$> pParenSep pExp' pExp' = Bin <$> (PAtom <$> sym (uncurry (||) . (&&&) (`elem` [VarIdent, ConsIdent]) isOperator) <*> pComments <|> pQvarsym) <*> pOpt pImpS -- | Parse simple type synonyms pType :: Parser TT (Exp TT) pType = PType <$> (Bin <$> pAtom [Reserved Type] <*> pOpt (pAtom [Reserved Instance])) <*> (TC . Expr <$> pTypeExpr') <*> ppAtom [ReservedOp Equal] <*> (TC . Expr <$> pTypeExpr') -- | Parse data declarations pData :: Parser TT (Exp TT) pData = PData <$> pAtom [Reserved Data, Reserved NewType] <*> (TC . Expr <$> pTypeExpr') <*> pOpt (pDataRHS <|> pGadt) <*> pOpt pDeriving pGadt :: Parser TT (Exp TT) pGadt = pWhere pTypeDecl -- | Parse second half of the data declaration, if there is one pDataRHS :: Parser TT (Exp TT) pDataRHS = PData' <$> pAtom [ReservedOp Equal] <*> pConstrs -- | Parse a deriving pDeriving :: Parser TT (Exp TT) pDeriving = pKW [Reserved Deriving] (TC . Expr <$> pTypeExpr') pAtype :: Parser TT (Exp TT) pAtype = pAtype' <|> pErr pAtype' :: Parser TT (Exp TT) pAtype' = pTypeCons <|> pParen (many $ pExprElem []) <|> pBrack (many $ pExprElem []) pTypeCons :: Parser TT (Exp TT) pTypeCons = Bin <$> pAtom [ConsIdent] <*> please (pMany $ pAtom [VarIdent, ConsIdent]) pContext :: Parser TT (Exp TT) pContext = Context <$> pOpt pForAll <*> (TC <$> (pClass' <|> pParenSep pClass')) <*> ppAtom [ReservedOp DoubleRightArrow] where pClass' :: Parser TT (Exp TT) pClass' = Bin <$> pQtycon <*> (please pVarId <|> pParen ((:) <$> please pVarId <*> many pAtype')) -- | Parse for all pForAll :: Parser TT (Exp TT) pForAll = pKW [Reserved Forall] (Bin <$> pVars <*> ppAtom [Operator "."]) pConstrs :: Parser TT (Exp TT) pConstrs = Bin <$> (Bin <$> pOpt pContext <*> pConstr) <*> pMany (pOP [ReservedOp Pipe] (Bin <$> pOpt pContext <*> please pConstr)) pConstr :: Parser TT (Exp TT) pConstr = Bin <$> pOpt pForAll <*> (Bin <$> (Bin <$> (DC <$> pAtype) <*> (TC <$> pMany (strictF pAtype))) <*> pOpt st) <|> Bin <$> lrHs <*> pMany (strictF pAtype) <|> pErr where lrHs = pOP [Operator "!"] pAtype st = pEBrace (pTypeDecl `sepBy1` pBareAtom [Special ',']) -- named fields declarations -- | Parse optional strict variables strictF :: Parser TT (Exp TT) -> Parser TT (Exp TT) strictF a = Bin <$> pOpt (pAtom [Operator "!"]) <*> a -- | Exporting module pEModule ::Parser TT (Exp TT) pEModule = pKW [Reserved Module] $ please (Modid <$> exact [ConsIdent] <*> pComments) -- | Parse a Let expression pLet :: Parser TT (Exp TT) pLet = PLet <$> pAtom [Reserved Let] <*> pBlock pFunDecl <*> pOpt (pBareAtom [Reserved In]) -- | Parse a Do block pDo :: Parser TT (Exp TT) pDo = Bin <$> pAtom [Reserved Do] <*> pBlock (pExpr ((Special ';' : recognizedSometimes) \\ [ReservedOp LeftArrow])) -- | Parse part of a lambda binding. pLambda :: Parser TT (Exp TT) pLambda = Bin <$> pAtom [ReservedOp BackSlash] <*> (Bin <$> (Expr <$> pPattern) <*> please (pBareAtom [ReservedOp RightArrow])) -- | Parse an Of block pOf :: Parser TT (Exp TT) pOf = Bin <$> pAtom [Reserved Of] <*> pBlock pAlternative pAlternative = Bin <$> (Expr <$> pPattern) <*> please (pFunRHS (ReservedOp RightArrow)) -- | Parse classes and instances -- This is very imprecise, but shall suffice for now. -- At least is does not complain too often. pClass :: Parser TT (Exp TT) pClass = PClass <$> pAtom [Reserved Class, Reserved Instance] <*> (TC . Expr <$> pTypeExpr') <*> pOpt (please (pWhere pTopDecl)) -- use topDecl since we have associated types and such. -- | Parse some guards and a where clause pGuard :: Token -> Parser TT (Exp TT) pGuard equalSign = PGuard <$> some (PGuard' <$> pCAtom [ReservedOp Pipe] pEmpty <*> -- comments are by default parsed after this pExpr (recognizedSometimes -- these two symbols can appear in guards. \\ [ReservedOp LeftArrow, Special ',']) <*> please (pEq equalSign)) -- this must be -> if used in case -- | Right-hand-side of a function or case equation (after the pattern) pFunRHS :: Token -> Parser TT (Exp TT) pFunRHS equalSign = Bin <$> (pGuard equalSign <|> pEq equalSign) <*> pOpt (pWhere pFunDecl) pWhere :: Parser TT (Exp TT) -> Parser TT (Exp TT) pWhere p = PWhere <$> pAtom [Reserved Where] <*> please (pBlock p) <*> pMany pErr -- After a where there might "misaligned" code that do not "belong" to anything. -- Here we swallow it as errors. -- Note that this can both parse an equation and a type declaration. -- Since they can start with the same token, the left part is factored here. pDecl :: Bool -> Bool -> Parser TT (Exp TT) pDecl acceptType acceptEqu = Expr <$> ((Yuck $ Enter "missing end of type or equation declaration" $ pure []) <|> ((:) <$> pElem False recognizedSometimes <*> pToList (pDecl acceptType acceptEqu)) <|> ((:) <$> pBareAtom [Special ','] <*> pToList (pDecl acceptType False)) -- if a comma is found, then the rest must be a type -- declaration. <|> (if acceptType then pTypeEnding else empty) <|> (if acceptEqu then pEquEnding else empty)) where pTypeEnding = (:) <$> (TS <$> exact [ReservedOp DoubleColon] <*> pTypeExpr') <*> pure [] pEquEnding = (:) <$> pFunRHS (ReservedOp Equal) <*> pure [] pFunDecl = pDecl True True pTypeDecl = pDecl True False --pEquation = pDecl False True -- | The RHS of an equation. pEq :: Token -> Parser TT (Exp TT) pEq equalSign = RHS <$> pBareAtom [equalSign] <*> pExpr' -- | Parse many of something pMany :: Parser TT (Exp TT) -> Parser TT (Exp TT) pMany p = Expr <$> many p -- | Parse a some of something separated by the token (Special '.') pBlocks :: Parser TT r -> Parser TT [r] pBlocks p = p `sepBy1` exact [nextLine] -- | Parse a some of something separated by the token (Special '.'), or nothing --pBlocks' :: Parser TT r -> Parser TT (BL.BList r) pBlocks' p = pBlocks p <|> pure [] -- | Parse a block of some something separated by the tok (Special '.') pBlockOf :: Parser TT (Exp TT) -> Parser TT (Exp TT) pBlockOf p = Block <$> pBlockOf' (pBlocks p) -- see HACK above pBlock :: Parser TT (Exp TT) -> Parser TT (Exp TT) pBlock p = pBlockOf' (Block <$> pBlocks' p) <|> pEBrace (p `sepBy1` exact [Special ';'] <|> pure []) <|> (Yuck $ Enter "block expected" pEmptyBL) -- | Parse something surrounded by (Special '<') and (Special '>') pBlockOf' :: Parser TT a -> Parser TT a pBlockOf' p = exact [startBlock] *> p <* exact [endBlock] -- see HACK above -- note that, by construction, '<' and '>' will always be matched, so -- we don't try to recover errors with them. -- | Parse something that can contain a data, type declaration or a class pTopDecl :: Parser TT (Exp TT) pTopDecl = pFunDecl <|> pType <|> pData <|> pClass <|> pure emptyNode -- | A "normal" expression, where none of the following symbols are acceptable. pExpr' = pExpr recognizedSometimes recognizedSometimes = [ReservedOp DoubleDot, Special ',', ReservedOp Pipe, ReservedOp Equal, ReservedOp LeftArrow, ReservedOp RightArrow, ReservedOp DoubleRightArrow, ReservedOp BackSlash, ReservedOp DoubleColon ] -- | Parse an expression, as a concatenation of elements. pExpr :: [Token] -> Parser TT (Exp TT) pExpr at = Expr <$> pExprOrPattern True at -- | Parse an expression, as a concatenation of elements. pExprOrPattern :: Bool -> [Token] -> Parser TT [Exp TT] pExprOrPattern isExpresssion at = pure [] <|> ((:) <$> pElem isExpresssion at <*> pExprOrPattern True at) <|> ((:) <$> (TS <$> exact [ReservedOp DoubleColon] <*> pTypeExpr') <*> pure []) -- TODO: not really correct: in (x :: X , y :: Z), all after the -- first :: will be a "type". pPattern = pExprOrPattern False recognizedSometimes pExprElem = pElem True -- | Parse an "element" of an expression or a pattern. -- "at" is a list of symbols that, if found, should be considered errors. pElem :: Bool -> [Token] -> Parser TT (Exp TT) pElem isExpresssion at = pCParen (pExprOrPattern isExpresssion -- might be a tuple, so accept commas as noise (recognizedSometimes \\ [Special ','])) pEmpty <|> pCBrack (pExprOrPattern isExpresssion (recognizedSometimes \\ [ ReservedOp DoubleDot, ReservedOp Pipe , ReservedOp LeftArrow , Special ','])) pEmpty -- list thing <|> pCBrace (many $ pElem isExpresssion -- record: TODO: improve (recognizedSometimes \\ [ ReservedOp Equal, Special ',' , ReservedOp Pipe])) pEmpty <|> (Yuck $ Enter "incorrectly placed block" $ -- no error token, but the previous keyword will be one. (of, where, ...) pBlockOf (pExpr recognizedSometimes)) <|> (PError <$> recoverWith (sym $ flip elem $ isNoiseErr at) <*> errTok <*> pEmpty) <|> (PAtom <$> sym (`notElem` isNotNoise at) <*> pEmpty) <|> if isExpresssion then pLet <|> pDo <|> pOf <|> pLambda else empty -- TODO: support type expressions pTypeExpr at = many (pTypeElem at) pTypeExpr' = pTypeExpr (recognizedSometimes \\ [ReservedOp RightArrow, ReservedOp DoubleRightArrow]) pTypeElem :: [Token] -> Parser TT (Exp TT) pTypeElem at = pCParen (pTypeExpr (recognizedSometimes \\ [ ReservedOp RightArrow, ReservedOp DoubleRightArrow, -- might be a tuple, so accept commas as noise Special ','])) pEmpty <|> pCBrack pTypeExpr' pEmpty <|> pCBrace pTypeExpr' pEmpty -- TODO: this is an error: mark as such. <|> (Yuck $ Enter "incorrectly placed block" $ pBlockOf (pExpr recognizedSometimes)) <|> (PError <$> recoverWith (sym $ flip elem $ isNoiseErr at) <*> errTok <*> pEmpty) <|> (PAtom <$> sym (`notElem` isNotNoise at) <*> pEmpty) -- | List of things that always should be parsed as errors isNoiseErr :: [Token] -> [Token] isNoiseErr r = recoverableSymbols ++ r recoverableSymbols = recognizedSymbols \\ fmap Special "([{<>." -- We just don't recover opening symbols (only closing are "fixed"). -- Layout symbols "<>." are never recovered, because layout is -- constructed correctly. -- | List of things that should not be parsed as noise isNotNoise :: [Token] -> [Token] isNotNoise r = recognizedSymbols ++ r -- | These symbols are always properly recognized, and therefore they -- should never be accepted as "noise" inside expressions. recognizedSymbols = [ Reserved Let , Reserved In , Reserved Do , Reserved Of , Reserved Class , Reserved Instance , Reserved Deriving , Reserved Module , Reserved Import , Reserved Type , Reserved Data , Reserved NewType , Reserved Where] ++ fmap Special "()[]{}<>." -- | Parse parenthesis, brackets and braces containing -- an expression followed by possible comments pCParen, pCBrace, pCBrack :: Parser TT [Exp TT] -> Parser TT [TT] -> Parser TT (Exp TT) pCParen p c = Paren <$> pCAtom [Special '('] c <*> p <*> (recoverAtom <|> pCAtom [Special ')'] c) pCBrace p c = Paren <$> pCAtom [Special '{'] c <*> p <*> (recoverAtom <|> pCAtom [Special '}'] c) pCBrack p c = Paren <$> pCAtom [Special '['] c <*> p <*> (recoverAtom <|> pCAtom [Special ']'] c) pParen, pBrack :: Parser TT [Exp TT] -> Parser TT (Exp TT) pParen = flip pCParen pComments --pBrace = flip pCBrace pComments pBrack = flip pCBrack pComments -- pEBrace parse an opening brace, followed by zero comments -- then followed by an closing brace and some comments pEBrace p = Paren <$> pCAtom [Special '{'] pEmpty <*> p <*> (recoverAtom <|> pCAtom [Special '}'] pComments) -- | Create a special error token. (e.g. fill in where there is no -- correct token to parse) Note that the position of the token has to -- be correct for correct computation of node spans. errTok = mkTok <$> curPos where curPos = tB <$> lookNext tB Nothing = maxBound tB (Just x) = tokBegin x mkTok p = Tok (Special '!') 0 (startPosn {posnOfs = p})