{- | Restructure expressions with infix operators according to associativity and precedence. -} module Fixity(fixInfixList) where import Util.Extra(strPos) import Syntax hiding (TokenId) import SyntaxPos import TokenId(TokenId(..), t_flip) import IdKind(IdKind(..)) import Id(Id) import State import RenameLib import Error -- Just == Bind -- Nothing == Stack reorder :: [Exp TokenId] -> RenameMonad (Exp TokenId) reorder es = getExp [] [] es getExp :: [((InfixClass TokenId, Int), (Exp TokenId, Int))] -> [Exp TokenId] -> [Exp TokenId] -> RenameMonad (Exp TokenId) getExp ops exps (e:es) = case e of ExpConOp pos o -> fixTid Con o >>>= \ fix -> case fix of (InfixPre a,l) -> getExp (stackPrefix fix (ExpCon pos o):ops) exps es _ -> error ("Mistake in an infix constructor application (" ++show o++") at "++strPos (getPos e)) ExpVarOp pos o -> fixTid Var o >>>= \ fix -> case fix of (InfixPre a,l) -> getExp (stackPrefix fix (ExpVar pos o):ops) exps es _ -> error ("Mistake in an infix operator chain involving (" ++show o++") at "++strPos (getPos e)) _ -> getOp ops (e:exps) es getExp ops [] [] = error ("Problem with infix section at unknown location.") getExp ops (e:es) [] = error ("Problem with infix section at "++strPos (getPos e)) getOp :: [((InfixClass TokenId, Int), (Exp TokenId, Int))] -> [Exp TokenId] -> [Exp TokenId] -> RenameMonad (Exp TokenId) getOp ops exps [] = finish ops exps getOp ops exps ees@(ExpConOp pos op:es) = harder pos ops Con op >>>= \ lr -> case lr of Just (o,ops) -> getOp ops (rebuild o exps) ees Nothing -> stackInfix (ExpCon pos op) >>>= \ fop -> getExp (fop:ops) exps es getOp ops exps ees@(ExpVarOp pos op:es) = harder pos ops Var op >>>= \ lr -> case lr of Just (o,ops) -> getOp ops (rebuild o exps) ees Nothing -> stackInfix (ExpVar pos op) >>>= \ fop -> getExp (fop:ops) exps es getOp ops exps (e:es) = error ("Expected an infix operator at " ++ strPos (getPos e)) finish :: Num b1 => [((InfixClass id, b), (Exp id, b1))] -> [Exp id] -> d -> s -> (Exp id, s) finish [] [] = error "finish empty" finish [] [e] = unitS e finish [] _ = error "finish multiple expression" finish (o:ops) es = finish ops (rebuild o es) stackInfix :: Exp TokenId -> RenameMonad ((InfixClass TokenId, Int), (Exp TokenId, Int)) stackInfix op@(ExpVar _ o) = fixTid Var o >>>= \ fix -> unitS (fix,(op,2::Int)) stackInfix op@(ExpCon _ o) = fixTid Con o >>>= \ fix -> unitS (fix,(op,2::Int)) stackPrefix :: a -> a1 -> (a, (a1, Int)) stackPrefix fix op = (fix,(op,1::Int)) -- harder :: Pos -> [((InfixClass a,Int),(g,f))] -> IdKind -> e -- -> State (b,(e -> TokenId),c,d) RenameState (Maybe ((((InfixClass a),Int),(g,f)),[((InfixClass a,Int),(g,f))])) RenameState harder :: Pos -> [((InfixClass a, Int), (g, f))] -> IdKind -> TokenId -> RenameMonad (Maybe (((InfixClass a, Int), (g, f)), [((InfixClass a, Int), (g, f))])) harder pos [] kind op' = unitS Nothing harder pos (ipop@((inf,pri),(op,_)):ops) kind op' = fixTid kind op' >>>= \ (inf',pri') -> if pri > pri' then unitS (Just (ipop,ops)) else if pri == pri' then case inf of InfixDef -> unitS (Just (ipop,ops)) InfixL -> unitS (Just (ipop,ops)) InfixPre _ -> unitS (Just (ipop,ops)) InfixR -> unitS (Nothing) Infix -> renameError (ErrorRaw $ "Infix operator at " ++ strPos pos ++ " is non-associative.") (Just (ipop,ops)) else unitS Nothing rebuild :: Num b => ((InfixClass id, b1), (Exp id, b)) -> [Exp id] -> [Exp id] rebuild (_,(op,2)) (e1:e2:es) = ExpApplication (getPos op) [op,e2,e1]:es rebuild ((InfixPre fun,_) ,(op,_)) (e1:es) = ExpApplication (getPos op) [ExpVar (getPos op) fun,e1]:es rebuild (_,(op,n)) es = error ("Not enough arguments at " ++ strPos (getPos op)) {- Main function of the module. -} fixInfixList :: [Exp TokenId] -> RenameMonad (Exp TokenId) fixInfixList [] = error "I: fixInfix []" fixInfixList ees@(ExpVarOp pos op:es) = fixTid Var op >>>= \ fix -> case fix of (InfixPre a,l) -> reorder ees _ -> reorder es >>>= \ exp -> invertCheck pos op fix exp >>> unitS (ExpApplication pos [ExpVar pos t_flip, ExpVar pos op, exp]) -- desugaring with flip better than lambda for reading a trace fixInfixList ees@(ExpConOp pos op:es) = fixTid Con op >>>= \ fix -> case fix of (InfixPre a,l) -> reorder ees _ -> reorder es >>>= \ exp -> invertCheck pos op fix exp >>> unitS (ExpApplication pos [ExpVar pos t_flip, ExpCon pos op, exp]) -- desugaring with flip better than lambda for reading a trace fixInfixList ees = case last ees of ExpConOp pos op -> reorder (init ees) >>>= \ exp -> fixTid Con op >>>= \ fix -> invertCheck pos op fix exp >>> unitS (ExpApplication pos [ExpCon pos op,exp]) ExpVarOp pos op -> reorder (init ees) >>>= \ exp -> fixTid Var op >>>= \ fix -> invertCheck pos op fix exp >>> unitS (ExpApplication pos [ExpVar pos op,exp]) _ -> reorder ees -- 'invertCheck' checks for priority inversion in an operator section. invertCheck :: Show a1 => Pos -> a1 -> (InfixClass TokenId, Int) -> Exp TokenId -> RenameMonadEmpty invertCheck pos1 op1 (fix1,pri1) exp = case exp of ExpApplication _ (ExpVar pos2 op2: es) -> check Var pos2 op2 ExpApplication _ (ExpCon pos2 op2: es) -> check Con pos2 op2 _ -> unitS0 where check kind pos2 op2 = fixTid kind op2 >>>= \(fix2,pri2) -> if pri2 < pri1 then error ("Fixity problem:\n " ++show op1++" used at "++strPos pos1++" has precedence " ++show pri1++",\n " ++show op2++" used at "++strPos pos2++" has precedence " ++show pri2++".\n " ++"The partially applied operator "++show op1 ++" should have lower precedence\n " ++"than the fully-applied operator " ++show op2++" used inside the section.\n") else unitS0 {- --------------------------------------------------------------------------}