-- Reading rules from an (Flat)Curry file:

module ReadFlatTRS(readRules,readRulesAndData,readFlatCurryRules) where

import qualified TRS
import FlatCurry.Types as FC
import FlatCurry.Files (readFlatCurry)
import OrCaseLifter

----------------------------------------------------------------------------
-- Reading rules from a (Flat)Curry file:
readRules :: String -> IO [TRS.Rule]
readRules prog = do
  putStrLn $ "Reading rules from Curry program " ++ prog ++ "..."
  flatprog <- readFlatCurry prog
  return (fst (curry2rules flatprog))

-- Reading rules and data declarations from a (Flat)Curry file:
readRulesAndData :: String -> IO ([TRS.Rule],[TypeDecl])
readRulesAndData prog = do
  putStrLn $ "Reading rules from Curry program " ++ prog ++ "..."
  flatprog <- readFlatCurry prog
  return (curry2rules flatprog)

-- Read FlatCurry program and return it together with the rules
-- transformed into a TRS:
readFlatCurryRules :: String -> IO (Prog,[TRS.Rule])
readFlatCurryRules prog = do
  putStrLn $ "Reading rules from Curry program " ++ prog ++ "..."
  flatprog <- readFlatCurry prog
  return (flatprog, fst $ curry2rules flatprog)

curry2rules (Prog modname _ tdecls fdecls _) =
  if any TRS.containsApply crules
  then (TRS.addApplyRules crules, tdecls)
  else (crules, tdecls)
 where
  rules  = concatMap fdecl2rules (liftNestedOrCase (modname,"ORCASE_") fdecls)
  crules = if any TRS.containsChoice rules then TRS.addChoiceRules rules
                                           else rules

-- translate function declaration into rules:
fdecl2rules (FC.Func (_,fname) arity _ _ (External _)) =
  [TRS.Rule fname (genArgs arity) (TRS.Func TRS.Def "EXTERNAL" [])]
 where
  genArgs n = map TRS.Var [1..n]

fdecl2rules (FC.Func fname _ _ _ (FC.Rule lhs rhs)) =
  map patternrule2rule patternrules
 where
  patternrules = rule2equations (Comb FuncCall fname (map Var lhs)) rhs

  patternrule2rule (l,r) =
    let (TRS.Func _ f args) = transExp l
     in TRS.Rule f args (transExp r)

  transExp (FC.Var i) = TRS.Var i
  transExp (Lit (Intc i))   = TRS.Func TRS.Cons (show i) [] 
  transExp (Lit (Floatc f)) = TRS.Func TRS.Cons (show f) []
  transExp (Lit (Charc c))  = TRS.Func TRS.Cons ['\'',c,'\''] []
  transExp (Comb ct (_,f) args) =
    TRS.Func (if ct==FuncCall then TRS.Def else TRS.Cons) f (map transExp args)
  transExp (Free _ exp) = transExp exp
  transExp (Let _ _)    = error "Let not yet supported"
  transExp (FC.Or _ _)  = error "Or not yet supported"
  transExp (Case _ _ _) = error "Case not yet supported"

----------------------------------------------------------------------------

-- transform a rule consisting of a left- and a right-hand side
-- (represented as expressions) into a set of pattern matching rules:
rule2equations :: Expr -> Expr -> [(Expr,Expr)]
rule2equations lhs (FC.Or e1 e2) =
   rule2equations lhs e1 ++ rule2equations lhs e2
rule2equations lhs (Case ctype e bs) =
   if isVarExpr e then let Var i = e  in  caseIntoLhs lhs i bs
                  else [(lhs,Case ctype e bs)]
rule2equations lhs (Var i) = [(lhs,Var i)]
rule2equations lhs (Lit l) = [(lhs,Lit l)]
rule2equations lhs (Comb ct name args) = [(lhs,Comb ct name args)]
rule2equations lhs (Free vs e) = [(lhs,Free vs e)]
rule2equations lhs (Let bs e) = [(lhs,Let bs e)]

caseIntoLhs _ _ [] = []
caseIntoLhs lhs vi (Branch (Pattern c vs) e : bs) =
  rule2equations (substitute [vi] [shallowPattern2Expr c vs] lhs) e
  ++ caseIntoLhs lhs vi bs
caseIntoLhs lhs vi (Branch (LPattern lit) e : bs) =
  rule2equations (substitute [vi] [Lit lit] lhs) e
  ++ caseIntoLhs lhs vi bs

shallowPattern2Expr name vars = Comb ConsCall name (map (\i->Var i) vars)


-- (substitute vars exps expr) = expr[vars/exps]
-- i.e., replace all occurrences of vars by corresponding exps in the
-- expression expr
substitute vars exps expr = substituteAll vars exps 0 expr

-- (substituteAll vars exps base expr):
-- substitute all occurrences of variables by corresonding expressions:
-- * substitute all occurrences of var_i by exp_i in expr
--   (if vars=[var_1,...,var_n] and exps=[exp_1,...,exp_n])
-- * substitute all other variables (Var j) by (Var (base+j))
--
-- here we assume that the new variables in guards and case patterns
-- do not occur in the list "vars" of replaced variables!

substituteAll :: [Int] -> [Expr] -> Int -> Expr -> Expr
substituteAll vars exps b (Var i) = replaceVar vars exps i
  where replaceVar [] [] var = Var (b+var)
        replaceVar (v:vs) (e:es) var = if v==var then e
                                                 else replaceVar vs es var
substituteAll _  _  _ (Lit l) = Lit l
substituteAll vs es b (Comb combtype c exps) =
                 Comb combtype c (map (substituteAll vs es b) exps)
substituteAll vs es b (Let bindings exp) =
                 Let (map (\(x,t,e)->(x+b,t,substituteAll vs es b e)) bindings)
                     (substituteAll vs es b exp)
substituteAll vs es b (Free vars e) =
                 Free (map (\(v,t) -> (v+b,t)) vars) (substituteAll vs es b e)
substituteAll vs es b (FC.Or e1 e2) =
                 FC.Or (substituteAll vs es b e1) (substituteAll vs es b e2)
substituteAll vs es b (Case ctype e cases) =
   Case ctype (substituteAll vs es b e) (map (substituteAllCase vs es b) cases)

substituteAllCase vs es b (Branch (Pattern l pvs) e) =
                 Branch (Pattern l (map (+b) pvs)) (substituteAll vs es b e)
substituteAllCase vs es b (Branch (LPattern l) e) =
                 Branch (LPattern l) (substituteAll vs es b e)


-- Is the expression a guarded expressions?
isGuardedExpr :: Expr -> Bool
isGuardedExpr e = case e of
  Comb _ f _ -> f == ("Prelude","cond")
  _ -> False

-- Is the expression a variable?
isVarExpr :: Expr -> Bool
isVarExpr e = case e of
  Var _ -> True
  _ -> False


---------------------------------------------------------------------------
