packages feed

caml-parser-0.1.0.0: src/CamlParser/Parser/Expr.hs

{-# LANGUAGE OverloadedStrings #-}
module CamlParser.Parser.Expr where

import Control.Monad (void)
import Data.Functor (($>))
import Data.List (foldl')
import Text.Megaparsec hiding (Token)
import CamlParser.Lexer.Token
import CamlParser.Syntax.Location
import CamlParser.Syntax.Constant
import CamlParser.Syntax.Type
import CamlParser.Syntax.Pattern
import CamlParser.Syntax.Expr
import CamlParser.Parser.Combinators
import CamlParser.Parser.Pattern (parsePattern, parseAtomicPattern)
import CamlParser.Parser.Type (parseType)

parseExpr :: Parser Expr
parseExpr = parseLetExpr

locatedExpr :: Parser (ExprF Expr) -> Parser Expr
locatedExpr p = do
  Located _ loc1 <- lookAhead anySingle
  e <- p
  Located _ loc2 <- lookAhead anySingle
  return $ Expr e (loc1 <> loc2)

parseLetExpr :: Parser Expr
parseLetExpr = choice
  [ parseLetIn
  , parseFun
  , parseFunction
  , parseMatch
  , parseTry
  , parseIfExpr
  , parseWhile
  , parseFor
  , parseWhere
  , parseSeqExpr
  ]

parseLetIn :: Parser Expr
parseLetIn = locatedExpr $ do
  tok TokLet
  rec <- option False (tok TokRec $> True)
  binds <- parseBindingList
  tok TokIn
  body <- parseExpr
  return $ ELet rec binds body

parseBindingList :: Parser [(Pattern, Expr)]
parseBindingList = sepBy1 parseBinding (tok TokAnd)

parseBinding :: Parser (Pattern, Expr)
parseBinding = do
  pat <- parsePattern
  void equal
  e <- parseExpr
  return (pat, e)

parseFun :: Parser Expr
parseFun = locatedExpr $ do
  tok TokFun
  optionalBar
  cases <- parseFunCases
  return $ EFunction cases

parseFunction :: Parser Expr
parseFunction = locatedExpr $ do
  tok TokFunction
  optionalBar
  cases <- parseFunctionCases
  return $ EFunction (map (\(p, e) -> ([p], e)) cases)

parseMatch :: Parser Expr
parseMatch = locatedExpr $ do
  tok TokMatch
  e <- parseExpr
  tok TokWith
  optionalBar
  cases <- parseFunctionCases
  return $ EMatch e (map (\(p, e) -> (p, e)) cases)

parseTry :: Parser Expr
parseTry = locatedExpr $ do
  tok TokTry
  e <- parseExpr
  tok TokWith
  optionalBar
  cases <- parseTryCases
  return $ ETry e cases

parseFunCases :: Parser [([Pattern], Expr)]
parseFunCases = sepBy1 parseFunCase bar
  where
    parseFunCase = do
      pats <- some parseAtomicPattern
      void minusGreater
      e <- parseExpr
      return (pats, e)

parseFunctionCases :: Parser [(Pattern, Expr)]
parseFunctionCases = sepBy1 parseFunctionCase bar
  where
    parseFunctionCase = do
      pat <- parsePattern
      void minusGreater
      e <- parseExpr
      return (pat, e)

parseTryCases :: Parser [(Pattern, Expr)]
parseTryCases = sepBy1 parseTryCase bar
  where
    parseTryCase = do
      pat <- parsePattern
      void minusGreater
      e <- parseExpr
      return (pat, e)

parseIfExpr :: Parser Expr
parseIfExpr = locatedExpr $ do
  tok TokIf
  cond <- parseExpr
  tok TokThen
  then_ <- parseExpr
  (do tok TokElse
      else_ <- parseExpr
      return $ EIf cond then_ else_
   ) <|> return (EIf cond then_ (Expr (EConstruct0 "()") emptyLoc))

parseWhile :: Parser Expr
parseWhile = locatedExpr $ do
  tok TokWhile
  cond <- parseExpr
  tok TokDo
  body <- parseOptExpr
  tok TokDone
  return $ EWhile cond body

parseFor :: Parser Expr
parseFor = locatedExpr $ do
  tok TokFor
  i <- identP
  void equal
  start <- parseExpr
  up <- (tok TokTo $> True) <|> (tok TokDownto $> False)
  end <- parseExpr
  tok TokDo
  body <- parseOptExpr
  tok TokDone
  return $ EFor (Local i) start end up body

parseWhere :: Parser Expr
parseWhere = do
  e <- parseSeqExpr
  (do tok TokWhere
      rec <- option False (tok TokRec $> True)
      binds <- parseBindingList
      return $ Expr (ELet rec binds e) (exprLoc e)
   ) <|> return e

parseSeqExpr :: Parser Expr
parseSeqExpr = do
  e <- parseAssignExpr
  (do semi
      e2 <- parseSeqExpr
      return $ Expr (ESeq e e2) (exprLoc e <> exprLoc e2)
   ) <|> return e

parseAssignExpr :: Parser Expr
parseAssignExpr = do
  e <- parseCommaExpr
  choice
    [ do assignP
         r <- parseAssignExpr
         return $ Expr (EAssign (extractId e) r) (exprLoc e)
    , do lessMinusP
         r <- parseAssignExpr
         return $ Expr (EApply (Expr (EIdent (Local "<-")) emptyLoc) [e, r]) (exprLoc e)
    , return e
    ]
  where
    extractId (Expr (EIdent (Local s)) _) = s
    extractId _ = ""

parseCommaExpr :: Parser Expr
parseCommaExpr = do
  es <- sepBy1 parseOrExpr comma
  case es of
    [e] -> return e
    _   -> return $ Expr (ETuple es) (foldl1 (<>) (map exprLoc es))

parseOrExpr :: Parser Expr
parseOrExpr = parseLeftAssoc [TokOr, TokBarBar] parseAndExpr mkBinop
  where
    mkBinop e1 e2 = Expr (EApply (Expr (EIdent (Local "or")) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parseAndExpr :: Parser Expr
parseAndExpr = parseLeftAssoc [TokAmpersand, TokAmpersandAmpersand] parseNotExpr mkBinop
  where
    mkBinop e1 e2 = Expr (EApply (Expr (EIdent (Local "&&")) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parseNotExpr :: Parser Expr
parseNotExpr = choice
  [ do tok TokNot
       e <- parseNotExpr
       return $ Expr (EApply (Expr (EIdent (Local "not")) emptyLoc) [e]) (exprLoc e)
  , parseCmpExpr
  ]

parseCmpExpr :: Parser Expr
parseCmpExpr = do
  e1 <- parseAppendExpr
  rest <- many $ choice
    [ do tok TokEqual; e2 <- parseAppendExpr; return ("=", e2)
    , do tok TokEqualEqual; e2 <- parseAppendExpr; return ("==", e2)
    , do op <- infixOpP 0; e2 <- parseAppendExpr; return (op, e2)
    ]
  return $ foldl' (\e (op, e2) -> mk op e e2) e1 rest
  where
    mk op e1 e2 = Expr (EApply (Expr (EIdent (Local op)) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parseAppendExpr :: Parser Expr
parseAppendExpr = parseRightAssocOp [1] parseConsExpr mkBinop
  where
    mkBinop op e1 e2 = Expr (EApply (Expr (EIdent (Local op)) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parseConsExpr :: Parser Expr
parseConsExpr = do
  e1 <- parseAddExpr
  (do consP
      e2 <- parseConsExpr
      return $ Expr (EApply (Expr (EIdent (Local "::")) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)
   ) <|> return e1

parseAddExpr :: Parser Expr
parseAddExpr = do
  e1 <- parseMulExpr
  rest <- many $ choice
    [ do s <- subtractiveP; e2 <- parseMulExpr; return (s, e2)
    , do op <- infixOpP 2; e2 <- parseMulExpr; return (op, e2)
    ]
  return $ foldl' (\e (op, e2) -> mk op e e2) e1 rest
  where
    mk op e1 e2 = Expr (EApply (Expr (EIdent (Local op)) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parseMulExpr :: Parser Expr
parseMulExpr = parseLeftAssocOp [3] parsePowExpr mkBinop
  where
    mkBinop op e1 e2 = Expr (EApply (Expr (EIdent (Local op)) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parsePowExpr :: Parser Expr
parsePowExpr = parseRightAssocOp [4] parseNegExpr mkBinop
  where
    mkBinop op e1 e2 = Expr (EApply (Expr (EIdent (Local op)) emptyLoc) [e1, e2]) (exprLoc e1 <> exprLoc e2)

parseNegExpr :: Parser Expr
parseNegExpr = choice
  [ do s <- subtractiveP
       e <- parseNegExpr
       case s of
         "-"  -> return $ Expr (EApply (Expr (EIdent (Local "-")) emptyLoc) [e]) (exprLoc e)
         "-." -> return $ Expr (EApply (Expr (EIdent (Local "-.")) emptyLoc) [e]) (exprLoc e)
         _    -> fail "unknown subtractive"
  , parseAppExpr
  ]

parseAppExpr :: Parser Expr
parseAppExpr = do
  e <- parseProjExpr
  args <- many parseProjExpr
  case args of
    [] -> return e
    _  -> return $ Expr (EApply e args) (exprLoc e <> foldl1 (<>) (map exprLoc args))

parseProjExpr :: Parser Expr
parseProjExpr = do
  e <- parsePrefixExpr
  projections e
  where
    projections e = choice
      [ do dot
           f <- identP
           projections (Expr (ERecordAccess e f) (exprLoc e))
      , do tok TokDotLParen
           i <- parseExpr
           tok TokRParen
           projections (Expr (EApply (Expr (EIdent (Local "vect_item")) emptyLoc) [e, i]) (exprLoc e))
      , do tok TokDotLBracket
           i <- parseExpr
           tok TokRBracket
           projections (Expr (EApply (Expr (EIdent (Local "nth_char")) emptyLoc) [e, i]) (exprLoc e))
      , return e
      ]

parsePrefixExpr :: Parser Expr
parsePrefixExpr = choice
  [ do op <- prefixOpP
       e <- parsePrefixExpr
       return $ Expr (EApply (Expr (EIdent (Local op)) emptyLoc) [e]) (exprLoc e)
  , parseAtomicExpr
  ]

parseAtomicExpr :: Parser Expr
parseAtomicExpr = locatedExpr $ choice
  [ EConstant <$> parseConstant
  , EIdent <$> parseIdent
  , parseListExpr
  , parseVectorExpr
  , parseStreamExpr
  , parseRecordExpr
  , parseUnit
  , do tok TokLParen
       e <- parseExpr
       tok TokRParen
       return $ unExpr e
  , do tok TokLParen
       e <- parseExpr
       tok TokColon
       t <- parseType
       tok TokRParen
       return $ EConstraint e t
  , parseBeginEnd
  ]

parseUnit :: Parser (ExprF Expr)
parseUnit = EConstruct0 "()" <$ try (tok TokLParen >> tok TokRParen)

parseBeginEnd :: Parser (ExprF Expr)
parseBeginEnd = tok TokBegin >> EConstruct0 "()" <$ tok TokEnd  -- TODO: proper begin..end

parseIdent :: Parser Ident
parseIdent = do
  s <- identP
  (do tok TokUnderUnder
      m <- identP
      return $ Global s m
   ) <|> return (Local s)

parseConstant :: Parser Constant
parseConstant = choice
  [ CInt <$> intP
  , CFloat <$> floatP
  , CString <$> stringP
  , CChar <$> charP
  ]

parseListExpr :: Parser (ExprF Expr)
parseListExpr = do
  es <- brackets (sepBy parseExpr semi)
  return $ foldr (\x r -> EApply (Expr (EIdent (Local "::")) emptyLoc) [x, Expr r emptyLoc]) (EConstruct0 "[]") es

parseVectorExpr :: Parser (ExprF Expr)
parseVectorExpr = do
  tok TokLBracketBar
  es <- sepBy parseExpr semi
  tok TokBarRBracket
  return $ EVector es

parseRecordExpr :: Parser (ExprF Expr)
parseRecordExpr = do
  fields <- braces (sepBy fieldP semi)
  return $ ERecord fields
  where
    fieldP = do
      f <- identP
      void equal
      e <- parseExpr
      return (f, e)

parseStreamExpr :: Parser (ExprF Expr)
parseStreamExpr = do
  tok TokLBracketLess
  comps <- sepBy parseStreamComponent semi
  tok TokGreaterRBracket
  return $ EStream comps

parseStreamComponent :: Parser (StreamComponent Expr)
parseStreamComponent = choice
  [ tok TokQuote >> STerm <$> parseExpr
  , SNonterm <$> parseExpr
  ]

parseOptExpr :: Parser Expr
parseOptExpr = parseExpr <|> return (Expr (EConstruct0 "()") emptyLoc)

parseLeftAssoc :: [Token] -> Parser Expr -> (Expr -> Expr -> Expr) -> Parser Expr
parseLeftAssoc toks next mk = do
  e1 <- next
  rest <- many ((choice (map tok toks)) >> next)
  return $ foldl' mk e1 rest

parseLeftAssocOp :: [Int] -> Parser Expr -> (String -> Expr -> Expr -> Expr) -> Parser Expr
parseLeftAssocOp levels next mk = do
  e1 <- next
  rest <- many ((do op <- choice (map infixOpP levels)
                    return op) >>= \op -> next >>= \e2 -> return (op, e2))
  return $ foldl' (\e (op, e2) -> mk op e e2) e1 rest

parseRightAssocOp :: [Int] -> Parser Expr -> (String -> Expr -> Expr -> Expr) -> Parser Expr
parseRightAssocOp levels next mk = do
  e1 <- next
  (do op <- choice (map infixOpP levels)
      e2 <- parseRightAssocOp levels next mk
      return $ mk op e1 e2
   ) <|> return e1