elm/compiler/Parse/Binops.hs

90 lines
3 KiB
Haskell
Raw Normal View History

module Parse.Binops (binops) where
2012-04-19 06:32:10 +00:00
import Ast
import Control.Monad (liftM,guard)
import Control.Monad.Error
import Data.List (foldl',splitAt,elemIndices
,group,groupBy,sortBy,find,intercalate)
2012-04-27 15:36:57 +00:00
import qualified Data.Map as Map
2012-04-19 06:32:10 +00:00
import Data.Maybe (mapMaybe)
import Located (epos)
import Text.Parsec
import Parse.Library
data Assoc = L | N | R deriving (Eq,Show)
2012-04-19 06:32:10 +00:00
type OpTable = [(Int, Assoc, String)]
table :: OpTable
table = [ (9, R, ".")
2012-08-07 11:39:03 +00:00
, (8, R, "^")
, (7, L, "*"), (7, L, "/"), (7, L, "mod"), (7, L, "div"), (7, L, "rem")
2012-04-19 06:32:10 +00:00
, (6, L, "+"), (6, L, "-")
, (5, R, "::"), (5, R, "++")
, (4, N, "<="), (4, N, ">="), (4, N, "<")
, (4, N, "=="), (4, N, "/="), (4, N, ">")
, (4, L, "~"), (4, L, "<~")
2012-04-19 06:32:10 +00:00
, (3, R, "&&")
, (2, R, "||")
, (0, R, "$")
, (0, R, "<|"), (0, L, "|>")
2012-04-19 06:32:10 +00:00
]
opLevel op = Map.findWithDefault 9 op dict
where dict = Map.fromList (map (\(lvl,_,op) -> (op,lvl)) table)
opAssoc op = Map.findWithDefault R op dict
where dict = Map.fromList (map (\(_,assoc,op) -> (op,assoc)) table)
hasLevel n (op,e) = opLevel op == n
sortOps :: OpTable -> OpTable
2012-04-19 06:32:10 +00:00
sortOps = sortBy (\(i,_,_) (j,_,_) -> compare i j)
binops :: IParser CExpr -> IParser String -> IParser CExpr
2012-04-19 06:32:10 +00:00
binops term anyOp = do
e <- term
split 0 e =<< many (commitIf (whitespace >> anyOp) $ do
whitespace ; op <- anyOp
whitespace ; e <- term
return (op,e))
split :: Int -> CExpr -> [(String, CExpr)] -> IParser CExpr
split _ e [] = return e
split n e eops = do
assoc <- getAssoc n eops
es <- sequence (splitLevel n e eops)
let ops = map fst (filter (hasLevel n) eops)
case assoc of R -> joinR es ops
_ -> joinL es ops
splitLevel :: Int -> CExpr -> [(String, CExpr)] -> [IParser CExpr]
splitLevel n e eops =
case break (hasLevel n) eops of
(lops, (op,e'):rops) -> split (n+1) e lops : splitLevel n e' rops
(lops, []) -> [ split (n+1) e lops ]
joinL :: [CExpr] -> [String] -> IParser CExpr
joinL [e] [] = return e
joinL (a:b:es) (op:ops) = joinL (epos a b (Binop op a b) : es) ops
joinL _ _ = fail "Ill-formed binary expression. Report a compiler bug."
joinR :: [CExpr] -> [String] -> IParser CExpr
joinR [e] [] = return e
joinR (a:b:es) (op:ops) = do e <- joinR (b:es) ops
return (epos a e (Binop op a e))
joinR _ _ = fail "Ill-formed binary expression. Report a compiler bug."
getAssoc :: Int -> [(String,CExpr)] -> IParser Assoc
getAssoc n eops | all (==L) assocs = return L
| all (==R) assocs = return R
| all (==N) assocs = case assocs of [_] -> return N
_ -> fail msg
where levelOps = filter (hasLevel n) eops
assocs = map (opAssoc . fst) levelOps
msg = concat [ "Conflicting precedence for binary operators ("
, intercalate ", " (map fst eops), "). "
, "Consider adding parentheses to disambiguate." ]