module Fay.Compiler.Pattern where
import Fay.Compiler.Misc
import Fay.Types
import Control.Monad.Error
import Control.Monad.State
import Control.Monad.Reader
import Data.List
import Data.Maybe
import Language.Haskell.Exts
compilePat :: JsExp -> Pat -> [JsStmt] -> Compile [JsStmt]
compilePat exp pat body =
case pat of
PVar name -> compilePVar name exp body
PApp cons pats -> compilePApp cons pats exp body
PLit literal -> compilePLit exp literal body
PParen pat -> compilePat exp pat body
PWildCard -> return body
pat@PInfixApp{} -> compileInfixPat exp pat body
PList pats -> compilePList pats body exp
PTuple pats -> compilePList pats body exp
PAsPat name pat -> compilePAsPat exp name pat body
PRec name pats -> compilePatFields exp name pats body
pat -> throwError (UnsupportedPattern pat)
compilePVar :: Name -> JsExp -> [JsStmt] -> Compile [JsStmt]
compilePVar name exp body = do
bindVar name
return $ JsVar (JsNameVar (UnQual name)) exp : body
compilePatFields :: JsExp -> QName -> [PatField] -> [JsStmt] -> Compile [JsStmt]
compilePatFields exp name pats body = do
c <- liftM (++ body) (compilePats' [] pats)
qname <- resolveName name
return [JsIf (force exp `JsInstanceOf` JsConstructor qname) c []]
where
compilePats' :: [QName] -> [PatField] -> Compile [JsStmt]
compilePats' names (PFieldPun name:xs) =
compilePats' names (PFieldPat (UnQual name) (PVar name):xs)
compilePats' names (PFieldPat fieldname (PVar varName):xs) = do
r <- compilePats' (fieldname : names) xs
bindVar varName
return $ JsVar (JsNameVar (UnQual varName))
(JsGetProp (force exp) (JsNameVar fieldname))
: r
compilePats' names (PFieldWildcard:xs) = do
records <- liftM stateRecords get
let fields = fromJust (lookup name records)
fields' = fields \\ names
f <- mapM (\fieldName -> do bindVar (unQual fieldName)
return (JsVar (JsNameVar fieldName)
(JsGetProp (force exp) (JsNameVar fieldName))))
fields'
r <- compilePats' names xs
return $ f ++ r
compilePats' _ [] = return []
compilePats' _ (pat:_) = throwError (UnsupportedFieldPattern pat)
unQual (Qual _ n) = n
unQual (UnQual n) = n
unQual Special{} = error "Trying to unqualify a Special..."
compilePLit :: JsExp -> Literal -> [JsStmt] -> Compile [JsStmt]
compilePLit exp literal body = do
c <- ask
lit <- readerCompileLit c literal
return [JsIf (equalExps exp lit)
body
[]]
where
equalExps :: JsExp -> JsExp -> JsExp
equalExps a b
| isConstant a && isConstant b = JsEq a b
| isConstant a = JsEq a (force b)
| isConstant b = JsEq (force a) b
| otherwise =
JsApp (JsName (JsBuiltIn "equal")) [a,b]
compilePAsPat :: JsExp -> Name -> Pat -> [JsStmt] -> Compile [JsStmt]
compilePAsPat exp name pat body = do
bindVar name
x <- compilePat exp pat body
return ([JsVar (JsNameVar (UnQual name)) exp] ++ x)
compilePApp :: QName -> [Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt]
compilePApp cons pats exp body = do
let forcedExp = force exp
let boolIf b = return [JsIf (JsEq forcedExp (JsLit (JsBool b))) body []]
case cons of
"True" -> boolIf True
"False" -> boolIf False
_ -> do
rf <- fmap (lookup cons) (gets stateRecords)
let recordFields =
fromMaybe
(error $ "Constructor '" ++ prettyPrint cons ++
"' was not found in stateRecords, did you try running this through GHC first?")
rf
substmts <- foldM (\body (field,pat) ->
compilePat (JsGetProp forcedExp (JsNameVar field)) pat body)
body
(reverse (zip recordFields pats))
qcons <- resolveName cons
return [JsIf (forcedExp `JsInstanceOf` JsConstructor qcons)
substmts
[]]
compilePList :: [Pat] -> [JsStmt] -> JsExp -> Compile [JsStmt]
compilePList [] body exp =
return [JsIf (JsEq (force exp) JsNull) body []]
compilePList pats body exp = do
let forcedExp = force exp
stmts <- foldM (\body (i,pat) -> compilePat (JsApp (JsName (JsBuiltIn "index"))
[JsLit (JsInt i),forcedExp])
pat
body)
body
(reverse (zip [0..] pats))
let patsLen = JsLit (JsInt (length pats))
return [JsIf (JsApp (JsName (JsBuiltIn "listLen")) [forcedExp,patsLen])
stmts
[]]
compileInfixPat :: JsExp -> Pat -> [JsStmt] -> Compile [JsStmt]
compileInfixPat exp pat@(PInfixApp left (Special cons) right) body =
case cons of
Cons -> do
withScopedTmpJsName $ \tmpName -> do
let forcedExp = JsName tmpName
x = JsGetProp forcedExp (JsNameVar "car")
xs = JsGetProp forcedExp (JsNameVar "cdr")
rightMatch <- compilePat xs right body
leftMatch <- compilePat x left rightMatch
return [JsVar tmpName (force exp)
,JsIf (JsInstanceOf forcedExp (JsBuiltIn "Cons"))
leftMatch
[]]
_ -> throwError (UnsupportedPattern pat)
compileInfixPat _ pat _ = throwError (UnsupportedPattern pat)