1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
------------------------------------------------------------------------
--- This module contains some operations to check and access
--- default rules in a Curry program.
---
--- @author Michael Hanus
--- @version November 2022
------------------------------------------------------------------------

module DefaultRuleUsage
  ( containsDefaultRules, checkDefaultRules
  , isDefaultFunc, isDefaultName, fromDefaultName
  )  where

import AbstractCurry.Types
import AbstractCurry.Select
import Data.List

--- Does a program contains default rules?
containsDefaultRules :: CurryProg -> Bool
containsDefaultRules = not . null . filter isDefaultFunc . functions

--- Check correct usage of default rules and return function names and errors
--- for incorrect uses.
checkDefaultRules :: CurryProg -> [(QName,String)]
checkDefaultRules prog =
  let allfunctions = functions prog
      (defruledecls,fdecls) = partition isDefaultFunc allfunctions
   in concatMap (checkDefaultRule fdecls) defruledecls ++
      concatMap checkLocalDefaultRule allfunctions

checkDefaultRule :: [CFuncDecl] -> CFuncDecl -> [(QName,String)]
checkDefaultRule funcs (CmtFunc _ qf ar vis texp rules) =
  checkDefaultRule funcs (CFunc qf ar vis texp rules)
checkDefaultRule funcs (CFunc defqn@(mn,deffn) ar _ _ rules)
  | null rules
  = [(defqn,"Default rule without right-hand side!")]
  | length rules > 1
  = [(defqn,"More than one default rule for function '" ++ orgfn ++ "'!")]
  | otherwise
  = maybe [(defqn,"Default rule given but function '" ++ orgfn ++ "' not defined!")]
          (\fd -> if funcArity fd == ar
                    then []
                    else [(defqn,"Default rule has wrong arity!")])
          (find (\fd -> funcName fd == orgqn) funcs)
 where
  orgfn = fromDefaultName deffn
  orgqn = (mn, orgfn)

checkLocalDefaultRule :: CFuncDecl -> [(QName,String)]
checkLocalDefaultRule (CmtFunc _ qf ar vis texp rules) =
  checkLocalDefaultRule (CFunc qf ar vis texp rules)
checkLocalDefaultRule (CFunc defqn _ _ _ rules) =
  checkLocalRules (concatMap allLocalDecls rules)
 where
  checkLocalRules ldecls =
    map (\ (_,fn) -> (defqn, "Local default rule '" ++ fn ++ "' is not allowed!"))
        (filter (isDefaultName . snd) (concatMap funcNamesOfLDecl ldecls))

--- Get all local declarations of a rule.
allLocalDecls :: CRule -> [CLocalDecl]
allLocalDecls (CRule _ rhs) = localsInRHS rhs
 where
  localsInRHS (CSimpleRhs  _ ldecls) = concatMap localsInLDecls ldecls
  localsInRHS (CGuardedRhs _ ldecls) = concatMap localsInLDecls ldecls

  localsInLDecls ldecl = ldecl : case ldecl of
    CLocalFunc fd -> concatMap allLocalDecls (funcRules fd)
    CLocalPat _ e -> localsInRHS e
    CLocalVars _  -> []

--- Is this function a declaration of a default rule?
isDefaultFunc :: CFuncDecl -> Bool
isDefaultFunc = isDefaultName . snd . funcName

--- Is this the name of a specification?
isDefaultName :: String -> Bool
isDefaultName f = "'default" `isSuffixOf` f

--- Drop the default rule suffix "'default" from the name:
fromDefaultName :: String -> String
fromDefaultName f =
  let rf = reverse f
   in reverse (drop (if take 8 rf == "tluafed'" then 8 else 0) rf)

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