summaryrefslogtreecommitdiff
path: root/src/GF/Devel/Compile/Extend.hs
blob: a10f8d929e475bedf759fccb1291b4eea794f103 (plain)
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
----------------------------------------------------------------------
-- |
-- Module      : Extend
-- Maintainer  : AR
-- Stability   : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/05/30 21:08:14 $ 
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.18 $
--
-- AR 14\/5\/2003 -- 11\/11
-- 4/12/2007 this module is still very very messy... ----
--
-- The top-level function 'extendModule'
-- extends a module symbol table by indirections to the module it extends
-----------------------------------------------------------------------------

module GF.Devel.Compile.Extend (
  extendModule
  ) where

import GF.Devel.Grammar.Modules
import GF.Devel.Grammar.Judgements
import GF.Devel.Grammar.MkJudgements
import GF.Devel.Grammar.PrGF
import GF.Devel.Grammar.Lookup
import GF.Devel.Grammar.Macros

import GF.Infra.Ident

import GF.Data.Operations

import Data.List (nub)
import Data.Map
import Control.Monad

extendModule :: GF -> SourceModule -> Err SourceModule
extendModule gf nmo0 = do
  (name,mo) <- rebuildModule gf nmo0
  case mtype mo of

  ---- Just to allow inheritance in incomplete concrete (which are not
  ---- compiled anyway), extensions are not built for them.
  ---- Should be replaced by real control. AR 4/2/2005
    MTConcrete _ | not (isCompleteModule mo) -> return (name,mo)
    _  -> do
      mo' <- foldM (extOne name) mo (mextends mo) 
      return (name, mo') 
 where
   extOne name mo (n,cond) = do
     (m0,isCompl) <- do
        m <- lookupModule gf n

        -- test that the module types match, and find out if the old is complete
        testErr True ---- (mtype mo == mtype m) 
                    ("illegal extension type to module" +++ prt name)
        return (m, isCompleteModule m)

     -- build extension in a way depending on whether the old module is complete
     js0 <- extendMod isCompl n (isInherited cond) name (mjments m0) (mjments mo)

     -- if incomplete, throw away extension information
     let me' = mextends mo ----if isCompl then es else (filter ((/=n) . fst) es) 
     return $ mo {mextends = me', mjments = js0}

-- | When extending a complete module: new information is inserted,
-- and the process is interrupted if unification fails.
-- If the extended module is incomplete, its judgements are just copied.
extendMod :: Bool -> Ident -> (Ident -> Bool) -> Ident -> 
             MapJudgement -> MapJudgement -> Err MapJudgement
extendMod isCompl name cond base old new = foldM try new $ assocs old where
  try t i@(c,_) | not (cond c) = return t
  try t i@(c,_) = errIn ("constant" +++ prt c) $
                  tryInsert (extendAnyInfo isCompl name base) indirIf t i
  indirIf = if isCompl then indirInfo name else id

indirInfo :: Ident -> JEntry -> JEntry
indirInfo n info = Right $ case info of
  Right (k,b) -> (k,b) -- original link is passed
  Left j      -> (n,isConstructor j)

extendAnyInfo :: Bool -> Ident -> Ident -> JEntry -> JEntry -> Err JEntry
extendAnyInfo isc n o i j = 
  errIn ("building extension for" +++ prt n +++ "in" +++ prt o) $ case (i,j) of
    (Left j1,Left j2) -> liftM Left $ unifyJudgement j1 j2
    (Right (m1,b1), Right (m2,b2)) -> do
      testErr (b1 == b2) "inconsistent indirection status"
      testErr (m1 == m2) $ 
        "different sources of inheritance:" +++ show m1 +++ show m2
      return i
    _ -> Bad $ "cannot unify information in"---- ++++ prt i ++++ "and" ++++ prt j

tryInsert :: Ord a => (b -> b -> Err b) -> (b -> b) ->
             Map a b -> (a,b) -> Err (Map a b)
tryInsert unif indir tree z@(x, info) = case Data.Map.lookup x tree of
  Just info0 -> do
    info1 <- unif info info0
    return $ insert x info1 tree 
  _ -> return $ insert x (indir info) tree

-- | rebuilding instance + interface, and "with" modules, prior to renaming. 
-- AR 24/10/2003
rebuildModule :: GF -> SourceModule -> Err SourceModule
rebuildModule gr mo@(i,mi) = case mtype mi of
  MTInstance i0 -> do
          m1 <- lookupModule gr i0
          testErr (mtype m1 == MTInterface) 
                  ("interface expected as type of" +++ prt i0)
          js' <- extendMod False i0 (const True) i (mjments m1) (mjments mi)
          --- to avoid double inclusions, in instance I of I0 = J0 ** ...
          case mextends mi of
            [] -> return $ (i,mi {mjments = js'})
            j0s -> do 
              m0s <- mapM (lookupModule gr . fst) j0s ---- restricted?? 12/2007
              let notInM0 c _  = all (notMember c . mjments) m0s
              let js2 = filterWithKey notInM0 js'
              return $ (i,mi {mjments = js2})

    -- add the instance opens to an incomplete module "with" instances
    --      ModWith mt stat ext me ops -> do
    --    ModWith (Module mt stat fs_ me ops_ js_) (ext,incl) ops -> do

  _ -> case minstances mi of
    [((ext,incl),ops)] -> do
        let infs  = Prelude.map fst ops
        let stat' = Prelude.null [i | (_,i) <- minterfaces mi, notElem i infs]
        testErr stat' ("module" +++ prt i +++ "remains incomplete")
           --        Module mt0 _ fs me' ops0 js <- lookupModMod gr ext
        mo0 <- lookupModule gr ext
        let ops1 = nub $
                     mopens mi ++   -- N.B. mo0 has been name-resolved already
                     ops ++ 
                     [(n,o) | (n,o) <- mopens mo0, notElem o infs] ++
                     [(i,i) | i <- Prelude.map snd ops] ----
                     ----    ++ [oSimple i   | i <- map snd ops] ----

        --- check if me is incomplete
        let fs1 = union (mflags mi) (mflags mo0)  -- new flags have priority
        let js0 = [ci | ci@(c,_) <- assocs (mjments mo0), isInherited incl c]
        let js1 = fromList (assocs (mjments mi) ++ js0)
        return $ (i,mo0 {
          mflags = fs1, 
          mextends = mextends mi,
          mopens = ops1,
          mjments = js1
          })
    _ -> return (i,mi)