module Parse.Expr (def,term) where
import Ast
import Control.Applicative ((<$>), (<*>))
import Control.Monad
import Data.Char (isSymbol, isDigit)
import Data.List (foldl')
import Text.Parsec hiding (newline,spaces)
import Text.Parsec.Indent
import qualified Text.Pandoc as Pan
import Parse.Library
import Parse.Patterns
import Parse.Binops
import Guid
import Types.Types (Type (VarT), Scheme (Forall))
import System.IO.Unsafe
numTerm :: IParser Expr
numTerm = toExpr <$> (preNum <?> "number")
where toExpr n | '.' `elem` n = FloatNum (read n)
| otherwise = IntNum (read n)
preNum = (++) <$> many1 digit <*> option "" postNum
postNum = do try $ lookAhead (string "." >> digit)
string "."
('.':) <$> many1 digit
strTerm :: IParser Expr
strTerm = liftM Str . expecting "string" . betwixt '"' '"' . many $
backslashed <|> satisfy (/='"')
varTerm :: IParser Expr
varTerm = toVar <$> var <?> "variable"
toVar v = case v of "True" -> Boolean True
"False" -> Boolean False
_ -> Var v
chrTerm :: IParser Expr
chrTerm = Chr <$> betwixt '\'' '\'' (backslashed <|> satisfy (/='\''))
<?> "character"
listTerm :: IParser Expr
listTerm = (do { try $ string "[markdown|"
; md <- filter (/='\r') <$> manyTill anyChar (try $ string "|]")
; return . Markdown $ Pan.readMarkdown Pan.defaultParserState md })
<|> (braces $ choice
[ try $ do { lo <- expr; whitespace; string ".." ; whitespace
; Range lo <$> expr }
, list <$> commaSep expr ])
parensTerm :: IParser Expr
parensTerm = parens $ choice
[ do op <- anyOp
return . Lambda "x" . Lambda "y" $ Binop op (Var "x") (Var "y")
, do let comma = char ',' <?> "comma ','"
commas <- comma >> many (whitespace >> comma)
let vars = map (('v':) . show) [ 0 .. length commas + 1 ]
return $ foldr Lambda (tuple $ map Var vars) (vars)
, do es <- commaSep expr
return $ case es of { [e] -> e; _ -> tuple es }
]
term :: IParser Expr
term = choice [ numTerm, strTerm, chrTerm
, accessible varTerm
, listTerm, parensTerm ]
<?> "basic term (4, x, 'c', etc.)"
appExpr :: IParser Expr
appExpr = do
tlist <- spaceSep1 term
return $ case tlist of
t:[] -> t
t:ts -> foldl' App t ts
binaryExpr :: IParser Expr
binaryExpr = binops appExpr anyOp
ifExpr :: IParser Expr
ifExpr = do reserved "if" ; whitespace ; e1 <- expr ; whitespace
reserved "then" ; whitespace ; e2 <- expr ; (whitespace <?> "an 'else' branch")
reserved "else" <?> "an 'else' branch" ; whitespace ; If e1 e2 <$> expr
lambdaExpr :: IParser Expr
lambdaExpr = do char '\\' <|> char '\x03BB' <?> "anonymous function"
whitespace
pats <- spaceSep1 patternTerm
whitespace ; arrow ; whitespace
e <- expr
return $ makeLambda pats e
defSet :: IParser [Def]
defSet = do
brace <- optionMaybe $ do
string "{" <?> "a set of definitions { x = ... ; y = ... }"
whitespace >> return "{"
case brace of
Nothing -> concat <$> block (do d <- assignExpr ; whitespace ; return d)
Just "{" -> do dss <- semiSep1 assignExpr
whitespace
string "}" <?> "closing bracket '}'"
return (concat dss)
letExpr :: IParser Expr
letExpr = do
reserved "let" ; whitespace
defs <- defSet
whitespace ; reserved "in" ; whitespace
Let defs <$> expr
caseExpr :: IParser Expr
caseExpr = do
reserved "case"; whitespace; e <- expr; whitespace; reserved "of"; whitespace
Case e <$> (with <|> without)
where case_ = do p <- patternExpr; whitespace; arrow; whitespace
(,) p <$> expr
with = brackets (semiSep1 (case_ <?> "cases { x -> ... }"))
without = block (do c <- case_ ; whitespace ; return c)
expr = choice [ ifExpr, letExpr, caseExpr
, lambdaExpr, binaryExpr ] <?> "an expression"
funcDef = try (do p1 <- try patternTerm ; infics p1 <|> func p1)
<|> ((:[]) <$> patternExpr)
<?> "the definition of a variable (x = ...)"
where func p@(PVar v) = (p:) <$> spacePrefix patternTerm
func p = do try (lookAhead (whitespace >> string "="))
return [p]
infics p1 = do
o:p <- try (whitespace >> anyOp)
p2 <- (whitespace >> patternTerm)
return $ if o == '`' then [ PVar $ takeWhile (/='`') p, p1, p2 ]
else [ PVar (o:p), p1, p2 ]
assignExpr :: IParser [Def]
assignExpr = withPos $ do
fDefs <- funcDef
whitespace
e <- (string "=" >> whitespace >> expr) <|> guardExpr
flattenPatterns fDefs e
guardExpr = (Guard <$> spaceSep1 gExpr)
where gExpr = do string "|" ; whitespace
b <- expr ; whitespace ; string "=" ; whitespace
(,) b <$> expr
def = map Definition <$> assignExpr
parseDef str =
case iParse def "" str of
Right result -> Right result
Left err -> Left $ "Parse error at " ++ show err