{-| Module : Language.Qux.Annotated.Parser Description : A Parsec indentation-based parser for generating a program. Copyright : (c) Henry J. Wylde, 2015 License : BSD3 Maintainer : public@hjwylde.com A "Text.Parsec" indentation-based parser for generating a 'Program'. -} module Language.Qux.Annotated.Parser ( -- * Parser type Parser, ParseError, parse, -- * Source position type SourcePos, sourceName, sourceLine, sourceColumn, -- * Parsers id_, program, decl, stmt, expr, value, type_ ) where import Control.Monad.State import Control.Monad.Trans.Except import Language.Qux.Annotated.Syntax import Language.Qux.Lexer import Text.Parsec hiding (State, parse) import Text.Parsec.Expr import Text.Parsec.Indent -- | A 'ParsecT' that retains indentation information. type Parser a = ParsecT String () (State SourcePos) a -- | @parse parser sourceName input@ parses @input@ using @parser@. -- Returns either a 'ParseError' or @a@. -- This method wraps 'runParserT' by running the indentation resolver over the parser's state. parse :: Parser a -> SourceName -> String -> Except ParseError a parse parser sourceName input = except $ runIndent sourceName (runParserT parser () sourceName input) -- | 'Id' parser. id_ :: Parser (Id SourcePos) id_ = Id <$> getPosition <*> identifier "identifier" -- | 'Program' parser. program :: Parser (Program SourcePos) program = do pos <- getPosition whiteSpace checkIndent reserved "module" module_ <- sepBy1 id_ dot checkIndent decls <- block decl eof return $ Program pos module_ decls "program" -- | 'Decl' parser. decl :: Parser (Decl SourcePos) decl = do pos <- getPosition name <- id_ symbol_ "::" parameters <- (try $ (,) <$> type_ <*> id_) `endBy` rightArrow returnType <- type_ colon indented stmts <- block stmt return $ FunctionDecl pos name (parameters ++ [(returnType, Id pos "@")]) stmts "function declaration" -- | 'Stmt' parser. stmt :: Parser (Stmt SourcePos) stmt = choice [ifStmt, returnStmt, whileStmt] "statement" where ifStmt = do pos <- getPosition reserved "if" condition <- expr colon indented trueStmts <- block stmt falseStmts <- option [] (checkIndent >> withBlock' (do { reserved "else"; colon }) stmt) return $ IfStmt pos condition trueStmts falseStmts returnStmt = ReturnStmt <$> getPosition <* reserved "return" <*> expr whileStmt = do pos <- getPosition withBlock (WhileStmt pos) (reserved "while" *> expr <* colon) stmt -- | 'Expr' parser. expr :: Parser (Expr SourcePos) expr = buildExpressionParser table (try application <|> term) "expression" application :: Parser (Expr SourcePos) application = ApplicationExpr <$> getPosition <*> id_ <*> many (sameOrIndented >> term) term :: Parser (Expr SourcePos) term = getPosition >>= \pos -> choice [ parens expr, ApplicationExpr pos <$> id_ <*> return [], ListExpr pos <$> brackets (expr `sepEndBy` comma), UnaryExpr pos Len <$> pipes expr, ValueExpr pos <$> value ] table :: OperatorTable String () (State SourcePos) (Expr SourcePos) table = [ [ Prefix (unaryExpr Neg "-") ], [ Infix (binaryExpr Acc "!!") AssocLeft ], [ Infix (binaryExpr Mul "*") AssocLeft, Infix (binaryExpr Div "/") AssocLeft, Infix (binaryExpr Mod "%") AssocLeft ], [ Infix (binaryExpr Add "+") AssocLeft, Infix (binaryExpr Sub "-") AssocLeft ], [ Infix (binaryExpr Lte "<=") AssocLeft, Infix (binaryExpr Lt "<") AssocLeft, Infix (binaryExpr Gte ">=") AssocLeft, Infix (binaryExpr Gt ">") AssocLeft ], [ Infix (binaryExpr Eq "==") AssocLeft, Infix (binaryExpr Neq "!=") AssocLeft ] ] binaryExpr :: BinaryOp -> String -> Parser ((Expr SourcePos) -> (Expr SourcePos) -> (Expr SourcePos)) binaryExpr op sym = getPosition >>= \pos -> BinaryExpr pos op <$ operator sym unaryExpr :: UnaryOp -> String -> Parser ((Expr SourcePos) -> (Expr SourcePos)) unaryExpr op sym = getPosition >>= \pos -> UnaryExpr pos op <$ operator sym -- | 'Value' parser. -- A value doesn't have a source position attached as this can be retrieved from a 'ValueExpr'. value :: Parser Value value = choice [ BoolValue False <$ reserved "false", BoolValue True <$ reserved "true", IntValue <$> natural, ListValue <$> brackets (value `sepEndBy` comma), NilValue <$ reserved "nil" ] "value" -- | 'Type' parser. type_ :: Parser (Type SourcePos) type_ = getPosition >>= \pos -> choice [ BoolType pos <$ reserved "Bool", IntType pos <$ reserved "Int", ListType pos <$> brackets type_, NilType pos <$ reserved "Nil" ] "type"