module Idris.Parser.Ops where
import Prelude hiding (pi)
import Text.Trifecta.Delta
import Text.Trifecta hiding (span, stringLiteral, charLiteral, natural, symbol, char, string, whiteSpace)
import Text.Parser.LookAhead
import Text.Parser.Expression
import qualified Text.Parser.Token as Tok
import qualified Text.Parser.Char as Chr
import qualified Text.Parser.Token.Highlight as Hi
import Idris.AbsSyntax
import Idris.Parser.Helpers
import Idris.Core.TT
import Control.Applicative
import Control.Monad
import Control.Monad.State.Strict
import Data.Maybe
import qualified Data.List.Split as Spl
import Data.List
import Data.Monoid
import Data.Char
import qualified Data.HashSet as HS
import qualified Data.Text as T
import qualified Data.ByteString.UTF8 as UTF8
import Debug.Trace
table :: [FixDecl] -> OperatorTable IdrisParser PTerm
table fixes
= [[prefix "-" (\fc x -> PApp fc (PRef fc [fc] (sUN "negate")) [pexp x])]] ++
toTable (reverse fixes) ++
[[backtick],
[binary "$" (\fc x y -> flatten $ PApp fc x [pexp y]) AssocRight],
[binary "=" (\fc x y -> PApp fc (PRef fc [fc] eqTy) [pexp x, pexp y]) AssocLeft],
[nofixityoperator]]
where
flatten :: PTerm -> PTerm
flatten (PApp fc (PApp _ f as) bs) = flatten (PApp fc f (as ++ bs))
flatten t = t
toTable :: [FixDecl] -> OperatorTable IdrisParser PTerm
toTable fs = map (map toBin)
(groupBy (\ (Fix x _) (Fix y _) -> prec x == prec y) fs)
where toBin (Fix (PrefixN _) op) = prefix op
(\fc x -> PApp fc (PRef fc [] (sUN op)) [pexp x])
toBin (Fix f op)
= binary op (\fc x y -> PApp fc (PRef fc [] (sUN op)) [pexp x,pexp y]) (assoc f)
assoc (Infixl _) = AssocLeft
assoc (Infixr _) = AssocRight
assoc (InfixN _) = AssocNone
binary :: String -> (FC -> PTerm -> PTerm -> PTerm) -> Assoc -> Operator IdrisParser PTerm
binary name f = Infix (do indentPropHolds gtProp
fc <- reservedOpFC name
indentPropHolds gtProp
return (f fc))
prefix :: String -> (FC -> PTerm -> PTerm) -> Operator IdrisParser PTerm
prefix name f = Prefix (do reservedOp name
fc <- getFC
indentPropHolds gtProp
return (f fc))
backtick :: Operator IdrisParser PTerm
backtick = Infix (do indentPropHolds gtProp
lchar '`'; (n, fc) <- fnName
lchar '`'
indentPropHolds gtProp
return (\x y -> PApp fc (PRef fc [fc] n) [pexp x, pexp y])) AssocNone
nofixityoperator :: Operator IdrisParser PTerm
nofixityoperator = Infix (do indentPropHolds gtProp
op <- try operator
unexpected $ "Operator without known fixity: " ++ op) AssocNone
operatorFront :: IdrisParser (Name, FC)
operatorFront = try (do (FC f (l, c) _) <- getFC
op <- lchar '(' *> reservedOp "=" <* lchar ')'
return (eqTy, FC f (l, c) (l, c+3)))
<|> maybeWithNS (do (FC f (l, c) _) <- getFC
op <- lchar '(' *> operator
(FC _ _ (l', c')) <- getFC
lchar ')'
return (op, (FC f (l, c) (l', c' + 1)))) False []
fnName :: IdrisParser (Name, FC)
fnName = try operatorFront <|> name <?> "function name"
fixity :: IdrisParser PDecl
fixity = do pushIndent
f <- fixityType; i <- fst <$> natural;
ops <- sepBy1 operator (lchar ',')
terminator
let prec = fromInteger i
istate <- get
let infixes = idris_infixes istate
let fs = map (Fix (f prec)) ops
let redecls = map (alreadyDeclared infixes) fs
let ill = filter (not . checkValidity) redecls
if null ill
then do put (istate { idris_infixes = nub $ sort (fs ++ infixes)
, ibc_write = map IBCFix fs ++ ibc_write istate
})
fc <- getFC
return (PFix fc (f prec) ops)
else fail $ concatMap (\(f, (x:xs)) -> "Illegal redeclaration of fixity:\n\t\""
++ show f ++ "\" overrides \"" ++ show x ++ "\"") ill
<?> "fixity declaration"
where alreadyDeclared :: [FixDecl] -> FixDecl -> (FixDecl, [FixDecl])
alreadyDeclared fs f = (f, filter ((extractName f ==) . extractName) fs)
checkValidity :: (FixDecl, [FixDecl]) -> Bool
checkValidity (f, fs) = all (== f) fs
extractName :: FixDecl -> String
extractName (Fix _ n) = n
checkDeclFixity :: IdrisParser PDecl -> IdrisParser PDecl
checkDeclFixity p = do decl <- p
case getDeclName decl of
Nothing -> return decl
Just n -> do checkNameFixity n
return decl
where getDeclName (PTy _ _ _ _ _ n _ _ ) = Just n
getDeclName (PData _ _ _ _ _ (PDatadecl n _ _ _)) = Just n
getDeclName _ = Nothing
checkNameFixity :: Name -> IdrisParser ()
checkNameFixity n = do fOk <- fixityOk n
unless fOk . fail $
"Missing fixity declaration for " ++ show n
where fixityOk (NS n' _) = fixityOk n'
fixityOk (UN n') | all (flip elem opChars) (str n') =
do fixities <- fmap idris_infixes get
return . elem (str n') . map (\ (Fix _ op) -> op) $ fixities
| otherwise = return True
fixityOk _ = return True
fixityType :: IdrisParser (Int -> Fixity)
fixityType = do reserved "infixl"; return Infixl
<|> do reserved "infixr"; return Infixr
<|> do reserved "infix"; return InfixN
<|> do reserved "prefix"; return PrefixN
<?> "fixity type"