module Parse.Expression (def,term,typeAnnotation) where import Control.Arrow ((***)) import Control.Applicative ((<$>), (<*>)) import Data.List (foldl') import Text.Parsec hiding (newline,spaces) import Text.Parsec.Indent import qualified Text.Pandoc as Pan import Parse.Helpers import qualified Parse.Pattern as Pattern import qualified Parse.Type as Type import Parse.Binop import Parse.Literal import SourceSyntax.Location as Location import SourceSyntax.Pattern hiding (tuple,list) import qualified SourceSyntax.Literal as Literal import SourceSyntax.Expression import SourceSyntax.Declaration (Declaration(Definition)) -------- Basic Terms -------- varTerm :: IParser (Expr t v) varTerm = toVar <$> var "variable" toVar :: String -> Expr t v toVar v = case v of "True" -> Literal (Literal.Boolean True) "False" -> Literal (Literal.Boolean False) _ -> Var v accessor :: IParser (Expr t v) accessor = do (start, lbl, end) <- located (try (string "." >> rLabel)) let loc e = Location.at start end e return (Lambda (PVar "_") (loc $ Access (loc $ Var "_") lbl)) negative :: IParser (Expr t v) negative = do (start, nTerm, end) <- located (try (char '-' >> notFollowedBy (char '.' <|> char '-')) >> term) let loc e = Location.at start end e return (Binop "-" (loc $ Literal (Literal.IntNum 0)) nTerm) -------- Complex Terms -------- listTerm :: IParser (Expr t v) listTerm = markdown' <|> braces (try range <|> ExplicitList <$> commaSep expr) where range = do lo <- expr whitespace >> string ".." >> whitespace Range lo <$> expr markdown' = do (rawText, exprs) <- markdown interpolation let md = Pan.readMarkdown Pan.def (filter (/='\r') rawText) return (Markdown md exprs) span xs = "{{ ... }}" interpolation md exprs = do try (string "{{") whitespace e <- expr whitespace string "}}" return (md ++ span exprs, exprs ++ [e]) parensTerm :: IParser (LExpr t v) parensTerm = try (parens opFn) <|> parens (tupleFn <|> parened) where opFn = do (start, op, end) <- located anyOp let loc = Location.at start end return . loc . Lambda (PVar "x") . loc . Lambda (PVar "y") . loc $ Binop op (loc $ Var "x") (loc $ Var "y") tupleFn = do let comma = char ',' "comma ','" (start, commas, end) <- located (comma >> many (whitespace >> comma)) let vars = map (('v':) . show) [ 0 .. length commas + 1 ] loc = Location.at start end return $ foldr (\x e -> loc $ Lambda x e) (loc . tuple $ map (loc . Var) vars) (map PVar vars) parened = do (start, es, end) <- located (commaSep expr) return $ case es of [e] -> e _ -> Location.at start end (tuple es) recordTerm :: IParser (LExpr t v) recordTerm = brackets $ choice [ misc, addLocation record ] where field = do label <- rLabel patterns <- spacePrefix Pattern.term whitespace >> string "=" >> whitespace body <- expr return (label, makeFunction patterns body) record = Record <$> commaSep field change = do lbl <- rLabel whitespace >> string "<-" >> whitespace (,) lbl <$> expr remove r = addLocation (string "-" >> whitespace >> Remove r <$> rLabel) insert r = addLocation $ do string "|" >> whitespace Insert r <$> rLabel <*> (whitespace >> string "=" >> whitespace >> expr) modify r = addLocation (string "|" >> whitespace >> Modify r <$> commaSep1 change) misc = try $ do record <- addLocation (Var <$> rLabel) whitespace opt <- optionMaybe (remove record) whitespace case opt of Just e -> try (insert e) <|> return e Nothing -> try (insert record) <|> try (modify record) term :: IParser (LExpr t v) term = addLocation (choice [ Literal <$> literal, listTerm, accessor, negative ]) <|> accessible (addLocation varTerm <|> parensTerm <|> recordTerm) "basic term (4, x, 'c', etc.)" -------- Applications -------- appExpr :: IParser (LExpr t v) appExpr = do t <- term ts <- constrainedSpacePrefix term $ \str -> if null str then notFollowedBy (char '-') else return () return $ case ts of [] -> t _ -> foldl' (\f x -> Location.merge f x $ App f x) t ts -------- Normal Expressions -------- binaryExpr :: IParser (LExpr t v) binaryExpr = binops appExpr lastExpr anyOp where lastExpr = addLocation (choice [ ifExpr, letExpr, caseExpr ]) <|> lambdaExpr ifExpr :: IParser (Expr t v) ifExpr = reserved "if" >> whitespace >> (normal <|> multiIf) where normal = do bool <- expr whitespace ; reserved "then" ; whitespace thenBranch <- expr whitespace "an 'else' branch" ; reserved "else" "an 'else' branch" ; whitespace elseBranch <- expr return $ MultiIf [(bool, thenBranch), (Location.sameAs elseBranch (Var "otherwise"), elseBranch)] multiIf = MultiIf <$> spaceSep1 iff where iff = do string "|" ; whitespace b <- expr ; whitespace ; string "->" ; whitespace (,) b <$> expr lambdaExpr :: IParser (LExpr t v) lambdaExpr = do char '\\' <|> char '\x03BB' "anonymous function" whitespace args <- spaceSep1 Pattern.term whitespace ; arrow ; whitespace body <- expr return (makeFunction args body) defSet :: IParser [Def t v] defSet = block (do d <- def ; whitespace ; return d) letExpr :: IParser (Expr t v) letExpr = do reserved "let" ; whitespace defs <- defSet whitespace ; reserved "in" ; whitespace Let defs <$> expr caseExpr :: IParser (Expr t v) caseExpr = do reserved "case"; whitespace; e <- expr; whitespace; reserved "of"; whitespace Case e <$> (with <|> without) where case_ = do p <- Pattern.expr; whitespace; arrow; whitespace (,) p <$> expr with = brackets (semiSep1 (case_ "cases { x -> ... }")) without = block (do c <- case_ ; whitespace ; return c) expr = addLocation (choice [ ifExpr, letExpr, caseExpr ]) <|> lambdaExpr <|> binaryExpr "an expression" defStart :: IParser [Pattern] defStart = choice [ do p1 <- try Pattern.term infics p1 <|> func p1 , func =<< (PVar <$> parens symOp) , (:[]) <$> Pattern.expr ] "the definition of a variable (x = ...)" where func pattern = case pattern of PVar v -> (pattern:) <$> spacePrefix Pattern.term _ -> do try (lookAhead (whitespace >> string "=")) return [pattern] infics p1 = do o:p <- try (whitespace >> anyOp) p2 <- (whitespace >> Pattern.term) return $ if o == '`' then [ PVar $ takeWhile (/='`') p, p1, p2 ] else [ PVar (o:p), p1, p2 ] makeFunction :: [Pattern] -> LExpr t v -> LExpr t v makeFunction args body@(L s _) = foldr (\arg body' -> L s $ Lambda arg body') body args definition :: IParser (Def t v) definition = withPos $ do (name:args) <- defStart whitespace >> string "=" >> whitespace body <- expr return . Def name $ makeFunction args body typeAnnotation :: IParser (Def t v) typeAnnotation = TypeAnnotation <$> try start <*> Type.expr where start = do v <- lowVar <|> parens symOp whitespace ; hasType ; whitespace return v def :: IParser (Def t v) def = typeAnnotation <|> definition