diff options
-rw-r--r-- | AST.hs | 48 | ||||
-rw-r--r-- | Main.hs | 26 | ||||
-rw-r--r-- | Parser.hs | 798 |
3 files changed, 437 insertions, 435 deletions
@@ -11,42 +11,42 @@ newtype Name = Name String deriving (Show, Eq) data Type - = TApp Type [Type] - | TTup [Type] - | TList Type - | TFun Type Type - | TCon Name - | TVar Name + = TApp Type [Type] + | TTup [Type] + | TList Type + | TFun Type Type + | TCon Name + | TVar Name deriving (Show) data FunEq t = FunEq Name [Pattern t] (RHS t) deriving (Show) data Pattern t - = PWildcard t - | PVar t Name - | PAs t Name (Pattern t) - | PCon t Name [Pattern t] - | PList t [Pattern t] - | PTup t [Pattern t] + = PWildcard t + | PVar t Name + | PAs t Name (Pattern t) + | PCon t Name [Pattern t] + | PList t [Pattern t] + | PTup t [Pattern t] deriving (Show) data RHS t - = Guarded [(Expr t, Expr t)] - | Plain (Expr t) + = Guarded [(Expr t, Expr t)] + | Plain (Expr t) deriving (Show) data Expr t - = ELit t Literal - | EVar t Name - | ECon t Name - | EList t [Expr t] - | ETup t [Expr t] - | EApp t (Expr t) [Expr t] - | EOp t (Expr t) Operator (Expr t) - | EIf t (Expr t) (Expr t) (Expr t) - | ECase t (Expr t) [(Pattern t, RHS t)] - | ELet t [FunDef t] (Expr t) + = ELit t Literal + | EVar t Name + | ECon t Name + | EList t [Expr t] + | ETup t [Expr t] + | EApp t (Expr t) [Expr t] + | EOp t (Expr t) Operator (Expr t) + | EIf t (Expr t) (Expr t) (Expr t) + | ECase t (Expr t) [(Pattern t, RHS t)] + | ELet t [FunDef t] (Expr t) deriving (Show) data Literal = LInt Integer | LFloat Rational | LChar Char | LString String @@ -10,18 +10,18 @@ import Parser main :: IO () main = do - (source, fname) <- getArgs >>= \case - [] -> (,"<stdin>") <$> getContents - [fname] -> (,fname) <$> readFile fname - _ -> die "Usage: hs-visinter [filename.hs]" + (source, fname) <- getArgs >>= \case + [] -> (,"<stdin>") <$> getContents + [fname] -> (,fname) <$> readFile fname + _ -> die "Usage: hs-visinter [filename.hs]" - prog <- case parse fname source of - This errs -> do - mapM_ (putStrLn . printErrMsg) errs - exitFailure - These errs res -> do - mapM_ (putStrLn . printErrMsg) errs - return res - That res -> return res + prog <- case parse fname source of + This errs -> do + mapM_ (putStrLn . printErrMsg) errs + exitFailure + These errs res -> do + mapM_ (putStrLn . printErrMsg) errs + return res + That res -> return res - print prog + print prog @@ -8,10 +8,10 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} module Parser ( - parse, - printErrMsg, - -- * Re-exports - These(..), + parse, + printErrMsg, + -- * Re-exports + These(..), ) where import Control.Applicative @@ -32,18 +32,18 @@ import AST -- See 'isInsideBlock' and 'isAtBlockLeft' for the two relevant "inside the -- block" conditions. data PS = PS - { psBlkLine :: Int -- ^ Start line of current layout block - , psBlkCol :: Int -- ^ Start column of current layout block - , psLine :: Int -- ^ Current line - , psCol :: Int -- ^ Current column - , psRest :: String -- ^ Rest of the input - } + { psBlkLine :: Int -- ^ Start line of current layout block + , psBlkCol :: Int -- ^ Start column of current layout block + , psLine :: Int -- ^ Current line + , psCol :: Int -- ^ Current column + , psRest :: String -- ^ Rest of the input + } deriving (Show) data Context = Context - { ctxFile :: FilePath - , ctxStack :: [String] -- ^ Stack of syntax scopes, for error reporting - } + { ctxFile :: FilePath + , ctxStack :: [String] -- ^ Stack of syntax scopes, for error reporting + } deriving (Show) -- ReaderT Context (ChronicleT [ErrMsg] (State PS) a) @@ -57,87 +57,88 @@ data Context = Context newtype Parser a = Parser { runParser :: Context -> PS -> These [ErrMsg] (PS, a) } instance Functor Parser where - fmap f (Parser g) = Parser (\ctx ps -> fmap (fmap f) (g ctx ps)) + fmap f (Parser g) = Parser (\ctx ps -> fmap (fmap f) (g ctx ps)) instance Applicative Parser where - pure x = Parser (\_ ps -> That (ps, x)) - (<*>) = ap + pure x = Parser (\_ ps -> That (ps, x)) + (<*>) = ap instance Monad Parser where - Parser g >>= f = Parser $ \ctx ps -> - case g ctx ps of - This errs -> This errs - That (ps', x) -> runParser (f x) ctx ps' - These errs (ps', x) -> case runParser (f x) ctx ps' of - This errs' -> This (errs <> errs') - That res -> These errs res - These errs' res -> These (errs <> errs') res + Parser g >>= f = Parser $ \ctx ps -> + case g ctx ps of + This errs -> This errs + That (ps', x) -> runParser (f x) ctx ps' + These errs (ps', x) -> case runParser (f x) ctx ps' of + This errs' -> This (errs <> errs') + That res -> These errs res + These errs' res -> These (errs <> errs') res instance Alternative Parser where - empty = Parser (\_ _ -> This mempty) - Parser f <|> Parser g = Parser $ \ctx ps -> - case f ctx ps of - This _ -> g ctx ps - success -> success + empty = Parser (\_ _ -> This mempty) + Parser f <|> Parser g = Parser $ \ctx ps -> + case f ctx ps of + This _ -> g ctx ps + success -> success instance MonadState PS Parser where - state f = Parser $ \_ ps -> That (swap (f ps)) + state f = Parser $ \_ ps -> That (swap (f ps)) instance MonadReader Context Parser where - reader f = Parser $ \ctx ps -> That (ps, f ctx) - local f (Parser g) = Parser (g . f) + reader f = Parser $ \ctx ps -> That (ps, f ctx) + local f (Parser g) = Parser (g . f) instance MonadChronicle [ErrMsg] Parser where - dictate errs = Parser (\_ ps -> These errs (ps, ())) - confess errs = Parser (\_ _ -> This errs) - memento (Parser f) = Parser (\ctx ps -> case f ctx ps of - This errs -> That (ps, Left errs) - That res -> That (Right <$> res) - These errs res -> These errs (Right <$> res)) - absolve def (Parser f) = Parser (\ctx ps -> case f ctx ps of - This _ -> That (ps, def) - success -> success) - condemn (Parser f) = Parser (\ctx ps -> case f ctx ps of - These errs _ -> This errs - res -> res) - retcon g (Parser f) = Parser (\ctx ps -> first g (f ctx ps)) - chronicle th = Parser (\_ ps -> (ps,) <$> th) + dictate errs = Parser (\_ ps -> These errs (ps, ())) + confess errs = Parser (\_ _ -> This errs) + memento (Parser f) = Parser (\ctx ps -> case f ctx ps of + This errs -> That (ps, Left errs) + That res -> That (Right <$> res) + These errs res -> These errs (Right <$> res)) + absolve def (Parser f) = Parser (\ctx ps -> case f ctx ps of + This _ -> That (ps, def) + success -> success) + condemn (Parser f) = Parser (\ctx ps -> case f ctx ps of + These errs _ -> This errs + res -> res) + retcon g (Parser f) = Parser (\ctx ps -> first g (f ctx ps)) + chronicle th = Parser (\_ ps -> (ps,) <$> th) -- Positions are zero-based in both dimensions -data ErrMsg = ErrMsg { errFile :: FilePath - , errStk :: [String] - , errLine :: Int - , errCol :: Int - , errMsg :: String } +data ErrMsg = ErrMsg + { errFile :: FilePath + , errStk :: [String] + , errLine :: Int + , errCol :: Int + , errMsg :: String } deriving (Show) printErrMsg :: ErrMsg -> String printErrMsg (ErrMsg fp stk y x s) = - unlines (map (\descr -> "In " ++ descr ++ ":") (reverse stk)) ++ - fp ++ ":" ++ show (y + 1) ++ ":" ++ show (x + 1) ++ ": " ++ s + unlines (map (\descr -> "In " ++ descr ++ ":") (reverse stk)) ++ + fp ++ ":" ++ show (y + 1) ++ ":" ++ show (x + 1) ++ ": " ++ s parse :: FilePath -> String -> These [ErrMsg] (Program ()) parse fp source = fmap snd $ runParser pProgram (Context fp []) (PS 0 0 0 0 source) pProgram :: Parser (Program ()) pProgram = do - prog <- Program <$> many pFunDef - skipWhiteComment - assertEOF Error - return prog + prog <- Program <$> many pFunDef + skipWhiteComment + assertEOF Error + return prog pFunDef :: Parser (FunDef ()) pFunDef = do - skipWhiteComment - pFunDef0 + skipWhiteComment + pFunDef0 pFunDef0 :: Parser (FunDef ()) pFunDef0 = do - mtypesig <- optional pStandaloneTypesig0 - let mname = fst <$> mtypesig - mtype = snd <$> mtypesig - (clauses, name) <- someClauses mname - return (FunDef name mtype clauses) + mtypesig <- optional pStandaloneTypesig0 + let mname = fst <$> mtypesig + mtype = snd <$> mtypesig + (clauses, name) <- someClauses mname + return (FunDef name mtype clauses) where someClauses :: Maybe Name -> Parser ([FunEq ()], Name) someClauses Nothing = do @@ -148,227 +149,227 @@ pFunDef0 = do -- | Given the name of the type signature, if any. pFunEq :: Maybe Name -> Parser (FunEq ()) pFunEq mCheckName = do - skipWhiteComment - assertAtBlockLeft Fatal "Expected function clause, found indented stuff" + skipWhiteComment + assertAtBlockLeft Fatal "Expected function clause, found indented stuff" - name <- pIdentifier0 AtLeft Lowercase - case mCheckName of - Just checkName | name /= checkName -> - raise Fatal "Name of function clause does not correspond with type signature" - _ -> return () + name <- pIdentifier0 AtLeft Lowercase + case mCheckName of + Just checkName | name /= checkName -> + raise Fatal "Name of function clause does not correspond with type signature" + _ -> return () - pats <- many (pPattern 11) - rhs <- pRHS "=" - return (FunEq name pats rhs) + pats <- many (pPattern 11) + rhs <- pRHS "=" + return (FunEq name pats rhs) -- | Pass "=" for function definitions and "->" for case clauses. pRHS :: String -> Parser (RHS ()) pRHS sepsym = do - -- TODO: parse guards - inlineWhite - pKeySym sepsym - Plain <$> pExpr + -- TODO: parse guards + inlineWhite + pKeySym sepsym + Plain <$> pExpr pPattern :: Int -> Parser (Pattern ()) pPattern d = inlineWhite >> pPattern0 d pPattern0 :: Int -> Parser (Pattern ()) pPattern0 d = do - asum [pPatWildcard0 - ,pPatVarOrAs0 - ,pPatCon0 - ,pPatList0 - ,pPatParens0] + asum [pPatWildcard0 + ,pPatVarOrAs0 + ,pPatCon0 + ,pPatList0 + ,pPatParens0] where pPatWildcard0 = pKeySym "_" >> return (PWildcard ()) pPatVarOrAs0 = do - var <- pIdentifier0 InBlock Lowercase - asum [do inlineWhite - pKeySym "@" - p <- pPattern 11 - return (PAs () var p) - ,return (PVar () var)] + var <- pIdentifier0 InBlock Lowercase + asum [do inlineWhite + pKeySym "@" + p <- pPattern 11 + return (PAs () var p) + ,return (PVar () var)] pPatCon0 = do - con <- pIdentifier0 InBlock Uppercase - if d > 0 - then return (PCon () con []) - else do args <- many (pPattern 11) - return (PCon () con args) + con <- pIdentifier0 InBlock Uppercase + if d > 0 + then return (PCon () con []) + else do args <- many (pPattern 11) + return (PCon () con args) pPatList0 = do - char '[' -- special syntax, no need for pKeySym - ps <- pPattern 0 `sepBy` (inlineWhite >> char ',') - inlineWhite - char ']' - return (PList () ps) + char '[' -- special syntax, no need for pKeySym + ps <- pPattern 0 `sepBy` (inlineWhite >> char ',') + inlineWhite + char ']' + return (PList () ps) pPatParens0 = do - char '(' - inlineWhite - asum [do char ')' - return (PTup () []) - ,do p <- pPattern0 0 - inlineWhite - asum [do char ')' - return p - ,do char ',' - ps <- pPattern 0 `sepBy1` (inlineWhite >> char ',') - return (PTup () (p : ps))]] + char '(' + inlineWhite + asum [do char ')' + return (PTup () []) + ,do p <- pPattern0 0 + inlineWhite + asum [do char ')' + return p + ,do char ',' + ps <- pPattern 0 `sepBy1` (inlineWhite >> char ',') + return (PTup () (p : ps))]] pExpr :: Parser (Expr ()) pExpr = do - inlineWhite - -- basics: lit, list, var, con, tup - -- expression atom: application of basics - -- expression parser: op - -- around: let, case, if - asum [pELet0 - ,pECase0 - ,pEIf0 - ,pExprOpExpr0 0] + inlineWhite + -- basics: lit, list, var, con, tup + -- expression atom: application of basics + -- expression parser: op + -- around: let, case, if + asum [pELet0 + ,pECase0 + ,pEIf0 + ,pExprOpExpr0 0] pELet0 :: Parser (Expr ()) pELet0 = do - pKeyword "let" + pKeyword "let" + inlineWhite + startLayoutBlock $ do + -- The first occurrence is also going to skip whitespace in front, + -- which is redundant -- but not harmful. + defs <- many $ do + skipWhiteComment + -- Note: now not necessarily in the indented block. Which is + -- precisely what we need here. If we see "in", let the 'many' + -- choice fail so that the defs loop ends. But let it fail outside + -- this asum so that it is the many that picks it up, not this + -- asum. + res <- asum [Nothing <$ lookAhead (pKeyword "in") + ,Just <$> pFunDef0] + case res of + Nothing -> empty + Just def -> return def inlineWhite - startLayoutBlock $ do - -- The first occurrence is also going to skip whitespace in front, - -- which is redundant -- but not harmful. - defs <- many $ do - skipWhiteComment - -- Note: now not necessarily in the indented block. Which is - -- precisely what we need here. If we see "in", let the 'many' - -- choice fail so that the defs loop ends. But let it fail outside - -- this asum so that it is the many that picks it up, not this - -- asum. - res <- asum [Nothing <$ lookAhead (pKeyword "in") - ,Just <$> pFunDef0] - case res of - Nothing -> empty - Just def -> return def - inlineWhite - body <- pExpr - return (ELet () defs body) + body <- pExpr + return (ELet () defs body) pECase0 :: Parser (Expr ()) pECase0 = do - pKeyword "case" - e <- pExpr - inlineWhite - pKeyword "of" - inlineWhite - startLayoutBlock $ do - -- The first clause is going to skip whitespace, but that's harmless - -- (though redundant). - let pClause = do - skipWhiteComment - whenM (not <$> isInsideBlock) (() <$ empty) - pat <- pPattern0 0 - rhs <- pRHS "->" - return (pat, rhs) - clauses <- many pClause - return (ECase () e clauses) + pKeyword "case" + e <- pExpr + inlineWhite + pKeyword "of" + inlineWhite + startLayoutBlock $ do + -- The first clause is going to skip whitespace, but that's harmless + -- (though redundant). + let pClause = do + skipWhiteComment + whenM (not <$> isInsideBlock) (() <$ empty) + pat <- pPattern0 0 + rhs <- pRHS "->" + return (pat, rhs) + clauses <- many pClause + return (ECase () e clauses) pEIf0 :: Parser (Expr ()) pEIf0 = do - pKeyword "if" - e1 <- pExpr - inlineWhite - pKeyword "then" - e2 <- pExpr - inlineWhite - pKeyword "else" - e3 <- pExpr - return (EIf () e1 e2 e3) + pKeyword "if" + e1 <- pExpr + inlineWhite + pKeyword "then" + e2 <- pExpr + inlineWhite + pKeyword "else" + e3 <- pExpr + return (EIf () e1 e2 e3) pExprOpExpr :: Int -> Parser (Expr ()) pExprOpExpr d = inlineWhite >> pExprOpExpr0 d pExprOpExpr0 :: Int -> Parser (Expr ()) pExprOpExpr0 d = do - e0 <- pEApp0 - climbRight e0 Nothing + e0 <- pEApp0 + climbRight e0 Nothing where climbRight :: Expr () -> Maybe ParsedOperator -> Parser (Expr ()) climbRight lhs mlhsop = do - asum [do paop@(PaOp op d2 a2) <- pInfixOp - guard (d2 >= d) -- respect global minimum precedence - case mlhsop of -- check operator compatibility - Just (PaOp _ d1 a1) -> - guard (d1 > d2 || (d1 == d2 && a1 == a2 && a1 /= AssocNone)) - Nothing -> - return () - let oprhsd = case a2 of AssocRight -> d2 ; _ -> d2 + 1 - rhs <- pExprOpExpr oprhsd - climbRight (EOp () lhs op rhs) (Just paop) - ,return lhs] + asum [do paop@(PaOp op d2 a2) <- pInfixOp + guard (d2 >= d) -- respect global minimum precedence + case mlhsop of -- check operator compatibility + Just (PaOp _ d1 a1) -> + guard (d1 > d2 || (d1 == d2 && a1 == a2 && a1 /= AssocNone)) + Nothing -> + return () + let oprhsd = case a2 of AssocRight -> d2 ; _ -> d2 + 1 + rhs <- pExprOpExpr oprhsd + climbRight (EOp () lhs op rhs) (Just paop) + ,return lhs] pEApp0 :: Parser (Expr ()) pEApp0 = do - e1 <- pEAtom0 - es <- many (inlineWhite >> pEAtom0) - case es of - [] -> return e1 - _ -> return (EApp () e1 es) + e1 <- pEAtom0 + es <- many (inlineWhite >> pEAtom0) + case es of + [] -> return e1 + _ -> return (EApp () e1 es) pEAtom0 :: Parser (Expr ()) pEAtom0 = (ELit () <$> pLiteral0) <|> pEList0 <|> pEVar0 <|> pECon0 <|> pEParens0 pLiteral0 :: Parser Literal pLiteral0 = asum - [do as <- some (satisfy isDigit) - let a = read as :: Integer - asum - [do char '.' - bs <- some (satisfy isDigit) - let b = read bs :: Integer - cs <- optional $ do - char 'e' - cs <- some (satisfy isDigit) - return cs - let c = maybe 0 read cs :: Integer - return (LFloat ((fromIntegral a + fromIntegral b / 10 ^ length bs) * 10 ^ c)) - ,return (LInt a)] - ,do char '\'' - c <- pStringChar - char '\'' - return (LChar c) - ,do char '"' - s <- many pStringChar - char '"' - return (LString s)] + [do as <- some (satisfy isDigit) + let a = read as :: Integer + asum + [do char '.' + bs <- some (satisfy isDigit) + let b = read bs :: Integer + cs <- optional $ do + char 'e' + cs <- some (satisfy isDigit) + return cs + let c = maybe 0 read cs :: Integer + return (LFloat ((fromIntegral a + fromIntegral b / 10 ^ length bs) * 10 ^ c)) + ,return (LInt a)] + ,do char '\'' + c <- pStringChar + char '\'' + return (LChar c) + ,do char '"' + s <- many pStringChar + char '"' + return (LString s)] pStringChar :: Parser Char pStringChar = asum - [do char '\\' - char 'x' - let hexdig = do - c <- satisfy $ \c' -> - let c = toLower c' - in 'a' <= c && c <= 'f' || '0' <= c && c <= '9' - return $ if 'a' <= c then 10 + ord c - ord 'a' - else ord c - ord '0' - digs <- some hexdig - return (chr (sum (zipWith (*) (reverse digs) (iterate (*16) 1)))) - ,do char '\\' - satisfy (const True) >>= \case - 'n' -> return '\n' - 'r' -> return '\r' - 't' -> return '\t' - 'a' -> return '\a' - 'b' -> return '\b' - '\'' -> return '\'' - '\"' -> return '\"' - '0' -> return '\0' - c -> do raise Error $ "Invalid escape sequence: \\" ++ [c] - return '?' - ,do satisfy (\c -> c `notElem` "\n\r\\\'")] + [do char '\\' + char 'x' + let hexdig = do + c <- satisfy $ \c' -> + let c = toLower c' + in 'a' <= c && c <= 'f' || '0' <= c && c <= '9' + return $ if 'a' <= c then 10 + ord c - ord 'a' + else ord c - ord '0' + digs <- some hexdig + return (chr (sum (zipWith (*) (reverse digs) (iterate (*16) 1)))) + ,do char '\\' + satisfy (const True) >>= \case + 'n' -> return '\n' + 'r' -> return '\r' + 't' -> return '\t' + 'a' -> return '\a' + 'b' -> return '\b' + '\'' -> return '\'' + '\"' -> return '\"' + '0' -> return '\0' + c -> do raise Error $ "Invalid escape sequence: \\" ++ [c] + return '?' + ,do satisfy (\c -> c `notElem` "\n\r\\\'")] pEList0 :: Parser (Expr ()) pEList0 = do - char '[' -- special syntax, no need for pKeySym - es <- sepBy pExpr (inlineWhite >> char ',') - inlineWhite - char ']' - return (EList () es) + char '[' -- special syntax, no need for pKeySym + es <- sepBy pExpr (inlineWhite >> char ',') + inlineWhite + char ']' + return (EList () es) pEVar0 :: Parser (Expr ()) pEVar0 = EVar () <$> pIdentifier0 InBlock Lowercase @@ -378,11 +379,11 @@ pECon0 = ECon () <$> pIdentifier0 InBlock Uppercase pEParens0 :: Parser (Expr ()) pEParens0 = do - char '(' - e <- pExpr - inlineWhite - char ')' - return e + char '(' + e <- pExpr + inlineWhite + char ')' + return e data Associativity = AssocLeft | AssocRight | AssocNone deriving (Show, Eq) @@ -392,40 +393,40 @@ data ParsedOperator = PaOp Operator Int Associativity pInfixOp :: Parser ParsedOperator pInfixOp = do - inlineWhite - asum [PaOp OEqu 4 AssocNone <$ pKeySym "==" - ,PaOp OAdd 6 AssocLeft <$ pKeySym "+" - ,PaOp OSub 6 AssocLeft <$ pKeySym "-" - ,PaOp OMul 7 AssocLeft <$ pKeySym "*" - ,PaOp ODiv 7 AssocLeft <$ pKeySym "/" - ,PaOp OMod 7 AssocLeft <$ pKeySym "%" - ,PaOp OPow 8 AssocRight <$ pKeySym "^" - ] + inlineWhite + asum [PaOp OEqu 4 AssocNone <$ pKeySym "==" + ,PaOp OAdd 6 AssocLeft <$ pKeySym "+" + ,PaOp OSub 6 AssocLeft <$ pKeySym "-" + ,PaOp OMul 7 AssocLeft <$ pKeySym "*" + ,PaOp ODiv 7 AssocLeft <$ pKeySym "/" + ,PaOp OMod 7 AssocLeft <$ pKeySym "%" + ,PaOp OPow 8 AssocRight <$ pKeySym "^" + ] pStandaloneTypesig0 :: Parser (Name, Type) pStandaloneTypesig0 = do - assertAtBlockLeft Fatal "Expected top-level definition, found indented stuff" - name@(Name namestr) <- pIdentifier0 AtLeft Lowercase - inlineWhite - pKeySym "::" - pushContext ("type signature for '" ++ namestr ++ "'") $ do - ty <- pType - return (name, ty) + assertAtBlockLeft Fatal "Expected top-level definition, found indented stuff" + name@(Name namestr) <- pIdentifier0 AtLeft Lowercase + inlineWhite + pKeySym "::" + pushContext ("type signature for '" ++ namestr ++ "'") $ do + ty <- pType + return (name, ty) pType :: Parser Type pType = do - ty1 <- pTypeApp - asum [do inlineWhite - pKeySym "->" - ty2 <- pType - return (TFun ty1 ty2) - ,return ty1] + ty1 <- pTypeApp + asum [do inlineWhite + pKeySym "->" + ty2 <- pType + return (TFun ty1 ty2) + ,return ty1] pTypeApp :: Parser Type pTypeApp = many pTypeAtom >>= \case - [] -> raise Error "Expected type" >> return (TTup []) - [t] -> return t - t:ts -> return (TApp t ts) + [] -> raise Error "Expected type" >> return (TTup []) + [t] -> return t + t:ts -> return (TApp t ts) pTypeAtom :: Parser Type pTypeAtom = pTypeParens <|> pTypeList <|> pTypeCon <|> pTypeVar @@ -438,9 +439,9 @@ pTypeAtom = pTypeParens <|> pTypeList <|> pTypeCon <|> pTypeVar return (TTup []) ,do ty1 <- pType ty2s <- many $ do - inlineWhite - char ',' - pType + inlineWhite + char ',' + pType inlineWhite char ')' case ty2s of @@ -448,11 +449,11 @@ pTypeAtom = pTypeParens <|> pTypeList <|> pTypeCon <|> pTypeVar _ -> return (TTup (ty1 : ty2s))] pTypeList = do - inlineWhite - char '[' - ty <- pType - char ']' - return (TList ty) + inlineWhite + char '[' + ty <- pType + char ']' + return (TList ty) pTypeCon = inlineWhite >> TCon <$> pIdentifier0 InBlock Uppercase pTypeVar = inlineWhite >> TVar <$> pIdentifier0 InBlock Lowercase @@ -460,14 +461,14 @@ pTypeAtom = pTypeParens <|> pTypeList <|> pTypeCon <|> pTypeVar -- | Parse the given name-like keyword, ensuring that it is the entire word. pKeyword :: String -> Parser () pKeyword s = do - string s - notFollowedBy (() <$ satisfy isNameContChar) + string s + notFollowedBy (() <$ satisfy isNameContChar) -- | Parse the given symbol-like keyword, ensuring that it is the entire symbol. pKeySym :: String -> Parser () pKeySym s = do - string s - notFollowedBy (() <$ satisfy isSymbolChar) + string s + notFollowedBy (() <$ satisfy isSymbolChar) data Case = Uppercase | Lowercase deriving (Show) @@ -481,12 +482,12 @@ pIdentifier0 bpos cs = pAlphaName0 bpos cs <|> pParens0 (pSymbol0 bpos cs) -- | Parser between parens, with the opening paren at the current position. pParens0 :: Parser a -> Parser a pParens0 p = do - char '(' - inlineWhite - res <- p - inlineWhite - char ')' - return res + char '(' + inlineWhite + res <- p + inlineWhite + char ')' + return res -- | Consumes a word-like name at the current position with the given case. The -- `varid` production in Haskell2010 for 'Lowercase', `conid' for 'Uppercase'. @@ -494,26 +495,27 @@ pIdentifier0 bpos cs = pAlphaName0 bpos cs <|> pParens0 (pSymbol0 bpos cs) -- > varid -> (small {small | large | digit | "'"}) without reservedid pAlphaName0 :: BlockPos -> Case -> Parser Name pAlphaName0 bpos cs = do - (_, s) <- readToken bpos - (\atfst mc -> case (atfst, mc) of - (True , Just c) | isNameHeadChar c -> Just (Right False) - (True , _ ) -> Nothing - (False, Just c) | isNameContChar c -> Just (Right False) - (False, _ ) -> Just (Left ())) - True - name <- case cs of - Uppercase | isLower (head s) -> do - raise Error "Unexpected uppercase word at this position, assuming typo" - return (toUpper (head s) : tail s) - Lowercase | isUpper (head s) -> do - raise Error "Unexpected lowercase word at this position, assuming typo" - return (toLower (head s) : tail s) - _ -> return s - guard (name `notElem` ["case", "class", "data", "default", "deriving", "do", "else" - ,"foreign", "if", "import", "in", "infix", "infixl" - ,"infixr", "instance", "let", "module", "newtype", "of" - ,"then", "type", "where", "_"]) - return (Name name) + (_, s) <- readToken + bpos + (\atfst mc -> case (atfst, mc) of + (True , Just c) | isNameHeadChar c -> Just (Right False) + (True , _ ) -> Nothing + (False, Just c) | isNameContChar c -> Just (Right False) + (False, _ ) -> Just (Left ())) + True + name <- case cs of + Uppercase | isLower (head s) -> do + raise Error "Unexpected uppercase word at this position, assuming typo" + return (toUpper (head s) : tail s) + Lowercase | isUpper (head s) -> do + raise Error "Unexpected lowercase word at this position, assuming typo" + return (toLower (head s) : tail s) + _ -> return s + guard (name `notElem` ["case", "class", "data", "default", "deriving", "do", "else" + ,"foreign", "if", "import", "in", "infix", "infixl" + ,"infixr", "instance", "let", "module", "newtype", "of" + ,"then", "type", "where", "_"]) + return (Name name) isNameHeadChar :: Char -> Bool isNameHeadChar c = isLetter c || c == '_' @@ -534,15 +536,15 @@ isNameContChar c = isNameHeadChar c || isDigit c || c == '\'' -- > reservedop -> ".." | ":" | "::" | "=" | "\" | "|" | "<-" | "->" | "@" | "~" | "=>" pSymbol0 :: BlockPos -> Case -> Parser Name pSymbol0 bpos cs = do - case bpos of - AtLeft -> assertAtBlockLeft Fatal "Expected symbol, but found indented expression" - InBlock -> assertInsideBlock Fatal "Expected symbol, but found end of indented expression" - name <- (:) <$> (case cs of Lowercase -> satisfy (\c -> isSymbolChar c && c /= ':') - Uppercase -> satisfy (== ':')) - <*> many (satisfy isSymbolChar) - guard (name `notElem` ["..", ":", "::", "=", "\\", "|", "<-", "->", "@", "~", "=>"]) - guard (take 2 name /= "--") - return (Name name) + case bpos of + AtLeft -> assertAtBlockLeft Fatal "Expected symbol, but found indented expression" + InBlock -> assertInsideBlock Fatal "Expected symbol, but found end of indented expression" + name <- (:) <$> (case cs of Lowercase -> satisfy (\c -> isSymbolChar c && c /= ':') + Uppercase -> satisfy (== ':')) + <*> many (satisfy isSymbolChar) + guard (name `notElem` ["..", ":", "::", "=", "\\", "|", "<-", "->", "@", "~", "=>"]) + guard (take 2 name /= "--") + return (Name name) isSymbolChar :: Char -> Bool isSymbolChar c = (isAscSymbol || isUniSymbol) && not isSpecialExt @@ -554,8 +556,8 @@ isSymbolChar c = (isAscSymbol || isUniSymbol) && not isSpecialExt sepBy1 :: Parser a -> Parser sep -> Parser [a] sepBy1 p psep = do - x1 <- p - (psep >> (x1 :) <$> sepBy1 p psep) <|> return [x1] + x1 <- p + (psep >> (x1 :) <$> sepBy1 p psep) <|> return [x1] sepBy :: Parser a -> Parser sep -> Parser [a] sepBy p psep = sepBy1 p psep <|> return [] @@ -564,32 +566,32 @@ sepBy p psep = sepBy1 p psep <|> return [] -- restored after completion of this subparser. startLayoutBlock :: Parser a -> Parser a startLayoutBlock p = do - ps0 <- get - put (ps0 { psBlkLine = psLine ps0 - , psBlkCol = psCol ps0 }) - res <- p - modify (\ps -> ps { psBlkLine = psBlkLine ps0 - , psBlkCol = psBlkCol ps0 }) - return res + ps0 <- get + put (ps0 { psBlkLine = psLine ps0 + , psBlkCol = psCol ps0 }) + res <- p + modify (\ps -> ps { psBlkLine = psBlkLine ps0 + , psBlkCol = psBlkCol ps0 }) + return res data Fatality fatal where - Error :: Fatality 'False - Fatal :: Fatality 'True + Error :: Fatality 'False + Fatal :: Fatality 'True deriving instance Show (Fatality fatal) type family FatalCtx fatal a where - FatalCtx 'False a = a ~ () - FatalCtx 'True a = () + FatalCtx 'False a = a ~ () + FatalCtx 'True a = () -- | Raise an error with the given fatality and description. raise :: FatalCtx fatal a => Fatality fatal -> String -> Parser a raise fat msg = do - Context { ctxFile = fp , ctxStack = stk } <- ask - PS { psLine = line, psCol = col } <- get - let fun = case fat of - Error -> dictate . pure - Fatal -> confess . pure - fun (ErrMsg fp stk line col msg) + Context { ctxFile = fp , ctxStack = stk } <- ask + PS { psLine = line, psCol = col } <- get + let fun = case fat of + Error -> dictate . pure + Fatal -> confess . pure + fun (ErrMsg fp stk line col msg) raise' :: Fatality fatal -> String -> Parser () raise' Error = raise Error @@ -609,39 +611,39 @@ data BlockPos = AtLeft | InBlock -- result is returned; otherwise, the parser fails. The newline is not consumed. readToken :: BlockPos -> (s -> Maybe Char -> Maybe (Either r s)) -> s -> Parser (r, String) readToken bpos f s0 = do - case bpos of - AtLeft -> assertAtBlockLeft Fatal "Expected token, but found indented expression" - InBlock -> assertInsideBlock Fatal "Expected token, but found end of indented expression" - let loop :: (s -> Maybe Char -> Maybe (Either r s)) -> s -> Parser (r, String) - loop f' st = do - ps <- get - case psRest ps of - [] | Just (Left res) <- f' st Nothing -> return (res, "") - | otherwise -> empty - '\n' : _ | Just (Left res) <- f' st (Just '\n') -> return (res, "") - c : cs -> case f' st (Just c) of - Nothing -> empty - Just (Left res) -> return (res, "") - Just (Right st') -> do - put (ps { psCol = psCol ps + 1, psRest = cs }) - fmap (c :) <$> loop f' st' - loop f s0 + case bpos of + AtLeft -> assertAtBlockLeft Fatal "Expected token, but found indented expression" + InBlock -> assertInsideBlock Fatal "Expected token, but found end of indented expression" + let loop :: (s -> Maybe Char -> Maybe (Either r s)) -> s -> Parser (r, String) + loop f' st = do + ps <- get + case psRest ps of + [] | Just (Left res) <- f' st Nothing -> return (res, "") + | otherwise -> empty + '\n' : _ | Just (Left res) <- f' st (Just '\n') -> return (res, "") + c : cs -> case f' st (Just c) of + Nothing -> empty + Just (Left res) -> return (res, "") + Just (Right st') -> do + put (ps { psCol = psCol ps + 1, psRest = cs }) + fmap (c :) <$> loop f' st' + loop f s0 -- | Consumes all whitespace and comments (including newlines), but only if -- this then leaves the parser inside the current block. If not, this fails. inlineWhite :: Parser () inlineWhite = do - skipWhiteComment - whenM (not <$> isInsideBlock) empty + skipWhiteComment + whenM (not <$> isInsideBlock) empty -- | Consumes all whitespace and comments (including newlines). Note: this may -- end outside the current block. skipWhiteComment :: Parser () skipWhiteComment = do - inlineSpaces - _ <- many (blockComment >> inlineSpaces) - optional_ lineComment - optional_ (consumeNewline >> skipWhiteComment) + inlineSpaces + _ <- many (blockComment >> inlineSpaces) + optional_ lineComment + optional_ (consumeNewline >> skipWhiteComment) -- | Consumes some inline whitespace. Stops before newlines. inlineSpaces :: Parser () @@ -651,22 +653,22 @@ inlineSpaces = readWhileInline isSpace -- end outside the current block. blockComment :: Parser () blockComment = do - string "{-" -- no need for pKeySym here - let loop = do - readWhileInline (`notElem` "{-") -- "-}" also starts with '-' - asum [string "-}" - ,eof >> raise Error "Unfinished {- -} comment at end of file" - ,blockComment >> loop - ,consumeNewline >> loop] - loop + string "{-" -- no need for pKeySym here + let loop = do + readWhileInline (`notElem` "{-") -- "-}" also starts with '-' + asum [string "-}" + ,eof >> raise Error "Unfinished {- -} comment at end of file" + ,blockComment >> loop + ,consumeNewline >> loop] + loop -- | Consumes a line comment marker and the rest of the line, excluding -- newline. lineComment :: Parser () lineComment = do - -- '--!' is an operator, so we need to parse a whole symbol here. - pKeySym "--" - readWhileInline (const True) + -- '--!' is an operator, so we need to parse a whole symbol here. + pKeySym "--" + readWhileInline (const True) -- | Raises an error if we're not currently at the given column. assertAtBlockLeft :: Fatality fatal -> String -> Parser () @@ -679,55 +681,55 @@ assertInsideBlock fat msg = whenM (not <$> isInsideBlock) $ raise' fat msg -- | Raises an error if we're not currently at EOF. assertEOF :: Fatality fatal -> Parser () assertEOF fat = gets psRest >>= \case - [] -> return () - _ -> raise' fat "Unexpected stuff" + [] -> return () + _ -> raise' fat "Unexpected stuff" -- | Returns whether the current position is _within_ the current block, for -- soft-wrapping content. This means that col > blkCol. isInsideBlock :: Parser Bool isInsideBlock = do - ps <- get - return $ psLine ps >= psBlkLine ps && psCol ps > psBlkCol ps + ps <- get + return $ psLine ps >= psBlkLine ps && psCol ps > psBlkCol ps -- | Returns whether the current position is at the left border of the block; -- this is for list content such as function definitions or let bindings. This -- means that col == blkCol. isAtBlockLeft :: Parser Bool isAtBlockLeft = do - ps <- get - return $ psLine ps >= psBlkLine ps && psCol ps == psBlkCol ps + ps <- get + return $ psLine ps >= psBlkLine ps && psCol ps == psBlkCol ps -- | Consumes characters while the predicate holds or until (and excluding) -- a newline, whichever comes first. readWhileInline :: (Char -> Bool) -> Parser () readWhileInline p = do - (taken, rest) <- span (\c -> p c && c /= '\n') <$> gets psRest - modify (\ps -> ps { psCol = psCol ps + length taken - , psRest = rest }) + (taken, rest) <- span (\c -> p c && c /= '\n') <$> gets psRest + modify (\ps -> ps { psCol = psCol ps + length taken + , psRest = rest }) -- | Consumes exactly one newline at the current position. consumeNewline :: Parser () consumeNewline = gets psRest >>= \case - '\n' : rest -> modify (\ps -> ps { psLine = psLine ps + 1 - , psCol = 0 - , psRest = rest }) - _ -> empty + '\n' : rest -> modify (\ps -> ps { psLine = psLine ps + 1 + , psCol = 0 + , psRest = rest }) + _ -> empty -- | Consumes exactly one character, unequal to newline, at the current position. satisfy :: (Char -> Bool) -> Parser Char satisfy p = do - traceM "entering satisfy" - r <- gets psRest - traceM "got rest" - r `seq` return () - traceM "seqd rest" - traceM ("rest is " ++ r) - case r of - c : rest | c /= '\n', p c -> do - modify (\ps -> ps { psCol = psCol ps + 1 - , psRest = rest }) - return c - _ -> empty + traceM "entering satisfy" + r <- gets psRest + traceM "got rest" + r `seq` return () + traceM "seqd rest" + traceM ("rest is " ++ r) + case r of + c : rest | c /= '\n', p c -> do + modify (\ps -> ps { psCol = psCol ps + 1 + , psRest = rest }) + return c + _ -> empty -- | Consumes exactly this character at the current position. Must not be a -- newline. @@ -739,25 +741,25 @@ char c = string [c] string :: String -> Parser () string s | any (== '\n') s = error "Newline in 'string' argument" string s = do - ps <- get - if take (length s) (psRest ps) == s - then put (ps { psCol = psCol ps + length s - , psRest = drop (length s) (psRest ps) }) - else empty + ps <- get + if take (length s) (psRest ps) == s + then put (ps { psCol = psCol ps + length s + , psRest = drop (length s) (psRest ps) }) + else empty lookAhead :: Parser () -> Parser () lookAhead p = do - ps <- get - success <- absolve False (True <$ p) - put ps -- restore state, as if nothing happened - when (not success) empty + ps <- get + success <- absolve False (True <$ p) + put ps -- restore state, as if nothing happened + when (not success) empty notFollowedBy :: Parser () -> Parser () notFollowedBy p = do - ps <- get - success <- absolve True (False <$ p) - put ps -- restore state, as if nothing happened - when (not success) empty + ps <- get + success <- absolve True (False <$ p) + put ps -- restore state, as if nothing happened + when (not success) empty -- | Only succeeds at EOF. eof :: Parser () |