2Module : Idris.Parser.Ops
3Description : Parser for operators and fixity declarations.
6Maintainer : The Idris Community.
8{-# LANGUAGE ConstraintKinds, FlexibleContexts, GeneralizedNewtypeDeriving,
10module Idris.Parser.Ops where
14import Idris.Parser.Helpers
16import Prelude hiding (pi)
18import Control.Applicative
19import Control.Arrow (first)
21import Control.Monad.State.Strict
22import Data.Char (isAlpha)
24import Data.List.NonEmpty (fromList)
25import Text.Megaparsec ((<?>))
26import qualified Text.Megaparsec as P
27import qualified Text.Megaparsec.Expr as P
29-- | Creates table for fixity declarations to build expression parser
30-- using pre-build and user-defined operator/fixity declarations
31table :: [FixDecl] -> [[P.Operator IdrisParser PTerm]]
33 = [[prefix "-" (\fc x -> PApp fc (PRef fc [fc] (sUN "negate")) [pexp x])]] ++
34 toTable (reverse fixes) ++
35 [[noFixityBacktickOperator],
36 [binary "$" P.InfixR $ \fc _ x y -> flatten $ PApp fc x [pexp y]],
37 [binary "=" P.InfixL $ \fc _ x y -> PApp fc (PRef fc [fc] eqTy) [pexp x, pexp y]],
40 flatten :: PTerm -> PTerm -- flatten application
41 flatten (PApp fc (PApp _ f as) bs) = flatten (PApp fc f (as ++ bs))
44 noFixityBacktickOperator :: P.Operator IdrisParser PTerm
45 noFixityBacktickOperator = P.InfixN $ do
46 (n, fc) <- backtickOperator
47 return $ \x y -> PApp fc (PRef fc [fc] n) [pexp x, pexp y]
49 -- | Operator without fixity (throws an error)
50 noFixityOperator :: P.Operator IdrisParser PTerm
51 noFixityOperator = P.InfixN $ do
53 op <- P.try symbolicOperator
54 P.unexpected . P.Label . fromList $ "Operator without known fixity: " ++ op
56 -- | Calculates table for fixity declarations
57 toTable :: [FixDecl] -> [[P.Operator IdrisParser PTerm]]
58 toTable fs = map (map toBin) (groupBy (\ (Fix x _) (Fix y _) -> prec x == prec y) fs)
60 toBin (Fix (PrefixN _) op) = prefix op $ \fc x ->
61 PApp fc (PRef fc [] (sUN op)) [pexp x]
62 toBin (Fix f op) = binary op (assoc f) $ \fc n x y ->
63 PApp fc (PRef fc [] n) [pexp x,pexp y]
65 assoc (Infixl _) = P.InfixL
66 assoc (Infixr _) = P.InfixR
67 assoc (InfixN _) = P.InfixN
69 isBacktick :: String -> Bool
70 isBacktick (c : _) = c == '_' || isAlpha c
73 binary :: String -> (IdrisParser (PTerm -> PTerm -> PTerm) -> P.Operator IdrisParser PTerm) -> (FC -> Name -> PTerm -> PTerm -> PTerm) -> P.Operator IdrisParser PTerm
75 | isBacktick name = ctor $ P.try $ do
76 (n, fc) <- backtickOperator
77 guard $ show (nsroot n) == name
79 | otherwise = ctor $ do
81 fc <- reservedOpFC name
83 return $ f fc (sUN name)
85 prefix :: String -> (FC -> PTerm -> PTerm) -> P.Operator IdrisParser PTerm
86 prefix name f = P.Prefix $ do
92{- | Parses a function used as an operator -- enclosed in backticks
100backtickOperator :: IdrisParser (Name, FC)
101backtickOperator = P.between (indentGt *> lchar '`') (indentGt *> lchar '`') name
103{- | Parses an operator name (either a symbolic name or a backtick-quoted name)
112operatorName :: IdrisParser (Name, FC)
113operatorName = first sUN <$> symbolicOperatorFC
116{- | Parses an operator in function position i.e. enclosed by `()', with an
122 | (Identifier_t '.')? '(' Operator_t ')'
127operatorFront :: IdrisParser (Name, FC)
128operatorFront = P.try (do (FC f (l, c) _) <- getFC
129 op <- lchar '(' *> reservedOp "=" <* lchar ')'
130 return (eqTy, FC f (l, c) (l, c+3)))
131 <|> maybeWithNS (do (FC f (l, c) _) <- getFC
132 op <- lchar '(' *> symbolicOperator
133 (FC _ _ (l', c')) <- getFC
135 return (op, (FC f (l, c) (l', c' + 1)))) False []
137{- | Parses a function (either normal name or operator)
140 FnName ::= Name | OperatorFront;
143fnName :: IdrisParser (Name, FC)
144fnName = P.try operatorFront <|> name <?> "function name"
146{- | Parses a fixity declaration
149 FixityType Natural_t OperatorList Terminator
153fixity :: IdrisParser PDecl
154fixity = do pushIndent
155 f <- fixityType; i <- fst <$> natural;
156 ops <- P.sepBy1 (show . nsroot . fst <$> operatorName) (lchar ',')
158 let prec = fromInteger i
160 let infixes = idris_infixes istate
161 let fs = map (Fix (f prec)) ops
162 let redecls = map (alreadyDeclared infixes) fs
163 let ill = filter (not . checkValidity) redecls
165 then do put (istate { idris_infixes = nub $ sort (fs ++ infixes)
166 , ibc_write = map IBCFix fs ++ ibc_write istate
169 return (PFix fc (f prec) ops)
170 else fail $ concatMap (\(f, (x:xs)) -> "Illegal redeclaration of fixity:\n\t\""
171 ++ show f ++ "\" overrides \"" ++ show x ++ "\"") ill
172 <?> "fixity declaration"
174 alreadyDeclared :: [FixDecl] -> FixDecl -> (FixDecl, [FixDecl])
175 alreadyDeclared fs f = (f, filter ((extractName f ==) . extractName) fs)
177 checkValidity :: (FixDecl, [FixDecl]) -> Bool
178 checkValidity (f, fs) = all (== f) fs
180 extractName :: FixDecl -> String
181 extractName (Fix _ n) = n
183-- | Check that a declaration of an operator also has fixity declared
184checkDeclFixity :: IdrisParser PDecl -> IdrisParser PDecl
185checkDeclFixity p = do decl <- p
186 case getDeclName decl of
187 Nothing -> return decl
188 Just n -> do checkNameFixity n
190 where getDeclName (PTy _ _ _ _ _ n _ _ ) = Just n
191 getDeclName (PData _ _ _ _ _ (PDatadecl n _ _ _)) = Just n
192 getDeclName _ = Nothing
194-- | Checks that an operator name also has a fixity declaration
195checkNameFixity :: Name -> IdrisParser ()
196checkNameFixity n = do fOk <- fixityOk n
198 "Missing fixity declaration for " ++ show n
199 where fixityOk (NS n' _) = fixityOk n'
200 fixityOk (UN n') | all (flip elem opChars) (str n') =
201 do fixities <- fmap idris_infixes get
202 return . elem (str n') . map (\ (Fix _ op) -> op) $ fixities
203 | otherwise = return True
204 fixityOk _ = return True
206{- | Parses a fixity declaration type (i.e. infix or prefix, associtavity)
216fixityType :: IdrisParser (Int -> Fixity)
217fixityType = do reserved "infixl"; return Infixl
218 <|> do reserved "infixr"; return Infixr
219 <|> do reserved "infix"; return InfixN
220 <|> do reserved "prefix"; return PrefixN
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list