support all other patterns and nested patterns
[clean-tests.git] / datatype / Language / Quote.hs
index 0153eaa..c6b94bc 100644 (file)
@@ -6,6 +6,7 @@ module Language.Quote where
 
 import Data.Char
 import Data.List
+import Data.Maybe
 import Debug.Trace
 
 import Control.Applicative
@@ -61,6 +62,9 @@ pSepBy sep el = (:) <$> el <*> many (sep *> el) <|> pure []
 pBrack :: (MonadFail m, Alternative m) => RParser m Token s -> RParser m Token s
 pBrack p = pSat (BOpen==) *> p <* pSat (BClose==)
 
+pCBrack :: (MonadFail m, Alternative m) => RParser m Token s -> RParser m Token s
+pCBrack p = pSat (COpen==) *> p <* pSat (CClose==)
+
 pCase :: (MonadFail m, Alternative m) => RParser m Token Exp
 pCase = mkCase <$ pSat (==Case) <*> pExp <* pSat (==Of) <*> some pCaseMatch
   where
@@ -68,22 +72,38 @@ pCase = mkCase <$ pSat (==Case) <*> pExp <* pSat (==Of) <*> some pCaseMatch
     mkCase name cases = foldr (uncurry mkCaseMatch) (VarE (mkName "bottom") `AppE` LitE (StringL "Exhausted case")) cases
       where
         mkCaseMatch :: Pat -> Exp -> Exp -> Exp
-        mkCaseMatch (VarP v) e _ = LetE [FunD v [Clause [] (NormalB name) []]] e
-        mkCaseMatch WildP e _ = e
-        mkCaseMatch (ConP consName fields) e rest
-            =      VarE (mkName "if'")
-            `AppE` (VarE (mkName $ "is" ++ stringName consName) `AppE` name) --Predicate
-            `AppE` LetE [mkFieldMatch idx f | f <- fields | idx <- [0 :: Int ..]] e
-            `AppE` rest
-          where
-            mkFieldMatch idx (VarP v) = FunD v [Clause [] (NormalB $ VarE (selectorName $ mkName $ stringName consName ++ "f" ++ show idx) `AppE` name) []]
---            mkFieldMatch idx p@(ConP consName fields) = FunD (mkName "f0") [Clause [] (NormalB $ mkCaseMatch p e (LitE (StringL "Exhausted case"))) []]
-            mkFieldMatch _ p = error $ "Unsupported subpat: " ++ show p
-
-        mkCaseMatch p _ _ = error $ "Unsupported pat: " ++ show p
+        mkCaseMatch a e rest = case mkCasePred name a of
+            Nothing -> LetE (mkCaseBinding name a []) e
+            Just p -> VarE (mkName "if'") `AppE` p `AppE` LetE (mkCaseBinding name a []) e `AppE` rest
+
+        mkCasePred :: Exp -> Pat -> Maybe Exp
+        mkCasePred objName (ConP consName fields) = Just $ foldl (ifx "&.")
+            (VarE (mkName $ "is" ++ stringName consName) `AppE` objName)
+            $ catMaybes $ [mkCasePred (VarE (selectorName $ mkName $ stringName consName ++ "f" ++ show idx) `AppE` objName) f | f <- fields | idx <- [0 :: Int ..]]
+        mkCasePred objName (RecP consName fields) = Just $ foldl (ifx "&.")
+            (VarE (mkName $ "is" ++ stringName consName) `AppE` objName)
+            $ catMaybes $ [mkCasePred (VarE (selectorName n) `AppE` objName) p | (n, p) <- fields]
+        mkCasePred _ (VarP _) = Nothing
+        mkCasePred _ WildP = Nothing
+        mkCasePred objName (ParensP p) = mkCasePred objName p
+        mkCasePred objName (AsP _ p) = mkCasePred objName p
+        mkCasePred objName (LitP v) = Just (ifx "==." (VarE (mkName "lit") `AppE` LitE v) objName)
+        mkCasePred _ p = error $ "Unsupported pat: " ++ show p
+
+        mkCaseBinding :: Exp -> Pat -> [Dec] -> [Dec]
+        mkCaseBinding objName (ConP consName fields) ds = foldr ($) ds $
+            [mkCaseBinding (VarE (selectorName $ mkName $ stringName consName ++ "f" ++ show idx) `AppE` objName) f | f <- fields | idx <- [0 :: Int ..]]
+        mkCaseBinding objName (RecP consName fields) ds = foldr ($) ds $
+            [mkCaseBinding (VarE (selectorName n) `AppE` objName) p| (n, p) <- fields]
+        mkCaseBinding objName (VarP v) ds = FunD v [Clause [] (NormalB $ objName) []]:ds
+        mkCaseBinding objName (AsP n v) ds = mkCaseBinding objName (VarP n) $ mkCaseBinding objName v ds
+        mkCaseBinding _ (LitP _) ds = ds
+        mkCaseBinding _ WildP ds = ds
+        mkCaseBinding objName (ParensP p) ds = mkCaseBinding objName p ds
+        mkCaseBinding _ p _ = error $ "Unsupported pat: " ++ show p
 
 pCaseMatch :: (MonadFail m, Alternative m) => RParser m Token (Pat, Exp)
-pCaseMatch = (,) <$> pPat <* pSat (==(Op "->")) <*> pExp
+pCaseMatch = (,) <$> pPat <* pSat (==(Op "->")) <*> pExp <* optional (pSat (==SColon))
 
 pExp :: (MonadFail m, Alternative m) => RParser m Token Exp
 pExp
@@ -114,18 +134,30 @@ pCon = mkName . uncon <$> pSat (\x->case x of Con _ -> True; _ -> False)
 
 pPat :: (MonadFail m, Alternative m) => RParser m Token Pat
 pPat
-    =   ConP <$> pCon <*> many pPat
+    =   RecP <$> pCon <*> pCBrack pFieldPat
+    <|> ConP <$> pCon <*> many pPat
+    <|> AsP <$> pVar <* pSat (At==) <*> pPat
     <|> VarP <$> pVar
     <|> WildP <$ pSat (Underscore==)
+    <|> LitP <$> pLit
     <|> pBrack pPat
+  where
+    pFieldPat = pSepBy (pSat (==Comma)) $
+        (,) <$> pVar <* pSat (==Equal) <*> pPat
 
 parseCP :: MonadFail m => [Char] -> m Exp
-parseCP s = case runParser pCase (lexer s) of
-    Nothing -> fail "Parsing failed"
-    Just (_, _:_) -> fail "Non-exhaustive parse found"
-    Just (e, []) -> pure e
-
-data Token = Lit {unlit :: Lit} | Con {uncon :: String} | Var {unvar :: String} | Case | Of | Op String | BOpen | BClose | Underscore | Unknown Char
+--parseCP s = case runParser pCase (lexer s) of
+parseCP s = case runParser pCase (lexer (trace (show s) s)) of
+--    Nothing -> fail $ "Parsing failed for: " ++ show (lexer s)
+    [] -> fail $ "Parsing failed for: " ++ show (lexer s)
+--    Just (_, t@(_:_)) -> fail $ "Non-exhaustive parse found: " ++ show t
+    (e, []):_ -> pure e
+--    x -> fail $ "Multiple parses: " ++ show x
+--    Just (e, []) -> pure e
+
+data Token = Lit {unlit :: Lit} | Con {uncon :: String} | Var {unvar :: String}
+    | Case | Of | Op String | BOpen | BClose | Underscore | SColon | At | COpen | CClose | Equal | Comma
+    | Unknown Char
   deriving (Eq, Show)
 
 lexer :: [Char] -> [Token]
@@ -148,6 +180,12 @@ lexer ('>':'.':rest) = Op ">.":lexer rest
 lexer ('(':rest) = BOpen:lexer rest
 lexer (')':rest) = BClose:lexer rest
 lexer ('_':rest) = Underscore:lexer rest
+lexer (';':rest) = SColon:lexer rest
+lexer ('@':rest) = At:lexer rest
+lexer ('{':rest) = COpen:lexer rest
+lexer ('}':rest) = CClose:lexer rest
+lexer ('=':rest) = Equal:lexer rest
+lexer (',':rest) = Comma:lexer rest
 lexer ('\'':'\\':x:'\'':rest) = case x of
         '\'' -> Lit (CharL '\''):lexer rest
         '\\' -> Lit (CharL '\\'):lexer rest
@@ -173,7 +211,7 @@ lexer (d:rest)
     | isAlpha d && isLower d = case span isAlpha rest of
         (s, rest') -> Var (d:s):lexer rest'
     | isDigit d || d == '-' || d == '+' = case span isDigit rest of
-        (s, rest') -> Lit (IntegerL $ read (d:s)):lexer rest'
+        (s, rest') -> trace (show (d:s)) $ Lit (IntegerL $ read (d:s)):lexer rest'
     | isSpace d = lexer rest
     | otherwise = Unknown d:lexer rest
 lexer [] = []