1--  Compiler Toolkit: general purpose attribute management
2--
3--  Author : Manuel M. T. Chakravarty
4--  Created: 14 February 95
5--
6--  Version $Revision: 1.4 $ from $Date: 2005/06/22 16:01:03 $
7--
8--  Copyright (c) [1995..1999] Manuel M. T. Chakravarty
9--
10--  This file is free software; you can redistribute it and/or modify
11--  it under the terms of the GNU General Public License as published by
12--  the Free Software Foundation; either version 2 of the License, or
13--  (at your option) any later version.
14--
15--  This file is distributed in the hope that it will be useful,
16--  but WITHOUT ANY WARRANTY; without even the implied warranty of
17--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18--  GNU General Public License for more details.
19--
20--- DESCRIPTION ---------------------------------------------------------------
21--
22--  This module provides an abstract notion of attributes (in the sense of
23--  compiler construction). The collection of attributes that is attached to a
24--  single node of the structure tree is referenced via an attributes
25--  identifier. This is basically a reference into so-called attribute tables,
26--  which manage attributes of one type and may use different representations.
27--  There is also a position attribute managed via the attribute identifier
28--  without needing a further table (it is already fixed on construction of
29--  the structure tree).
30--
31--  The `Attributed' class is based on a suggestion from Roman Lechtchinsky.
32--
33--- DOCU ----------------------------------------------------------------------
34--
35--  language: Haskell 98
36--
37--  * Attribute identifiers are generated during parsing and whenever new
38--    structure tree elements, possibly due to transformations, are generated.
39--
40--  * New attributes can be added by simply providing a new attribute table
41--    indexed by the attribute identifiers. Thus, adding or discarding an
42--    attribute does not involve any change in the structure tree.
43--
44--  * Consecutive sequences of names are used as attribute identifiers to
45--    facilitate the use of arrays for attributes that are fixed; speeds up
46--    read access. (See also TODO.)
47--
48--  * Each attribute table can simultaneously provide melted (updatable) and
49--    frozen (non-updatable) attributes. It also allows to dynamically grow the
50--    table, i.e., cover a wider range of attribute identifiers.
51--
52--  * There is a variant merely providing a position, which is used for
53--    internal identifiers and such.
54--
55--  * `StdAttr' provides standard undefined and don't care variants for
56--    attribute values.
57--
58--- TODO ----------------------------------------------------------------------
59--
60--  * When there are sparse attribute tables that we want to freeze (and they
61--    will occur sooner or later), then introduce a third variant of tables
62--    realized via hash table---depending on the type of attribute table, we
63--    may even allow them to be soft.
64--
65--    NOTE: Currently, if assertions are switched on, on freezing a table, its
66--          density is calculate and, if it is below 33%, an internal error is
67--          raised (only if there are more than 1000 entries in the table).
68--
69--  * check whether it would increase the performance significantly if we use
70--    a mixed finite map/array representation for soft tables (all attributes
71--    defined before the last `soften' could be held in the array, changing
72--    an attribute just means to update it in the FM; i.e., the FM entries take
73--    precedence over the array entries)
74--
75
76module Attributes (-- attribute management
77                   --
78                   Attrs, newAttrsOnlyPos, newAttrs,
79                   Attributed(attrsOf), eqOfAttrsOf, posOfAttrsOf,
80                   --
81                   -- attributes and attribute tables
82                   --
83                   Attr(undef, isUndef, dontCare, isDontCare),
84                   AttrTable, newAttrTable, getAttr, setAttr, updAttr,
85                   copyAttr, freezeAttrTable, softenAttrTable,
86                   StdAttr(..), getStdAttr, getStdAttrDft, isDontCareStdAttr,
87                   isUndefStdAttr, setStdAttr, updStdAttr,
88                   getGenAttr, setGenAttr, updGenAttr)
89where
90
91import Data.Array
92import Control.Exception (assert)
93import Position   (Position, Pos(posOf), nopos, isNopos, dontCarePos,
94                   isDontCarePos)
95import Errors     (interr)
96import UNames     (NameSupply, Name,
97                   rootSupply, splitSupply, names)
98import Map        (Map)
99import qualified  Map as Map (fromList, toList, insert,
100                   findWithDefault, empty)
101import Binary     (Binary(..), putByte, getByte)
102
103
104-- attribute management data structures and operations
105-- ---------------------------------------------------
106
107-- abstract data structure used in the structure tree to represent the
108-- attribute identifier and the position (EXPORTED)
109--
110data Attrs = OnlyPos Position           -- only pos (for internal stuff only)
111           | Attrs   Position Name      -- pos and unique name
112
113-- get the position associated with an attribute identifier (EXPORTED)
114--
115instance Pos Attrs where
116  posOf (OnlyPos pos  ) = pos
117  posOf (Attrs   pos _) = pos
118
119-- equality of attributes is used to define the equality of objects (EXPORTED)
120--
121instance Eq Attrs where
122  (Attrs   _ id1) == (Attrs   _ id2) = id1 == id2
123  _               == _               =
124    interr "Attributes: Attempt to compare `OnlyPos' attributes!"
125
126-- attribute ordering is also lifted to objects (EXPORTED)
127--
128instance Ord Attrs where
129  (Attrs   _ id1) <= (Attrs   _ id2) = id1 <= id2
130  _               <= _               =
131    interr "Attributes: Attempt to compare `OnlyPos' attributes!"
132
133-- a class for convenient access to the attributes of an attributed object
134-- (EXPORTED)
135--
136class Attributed a where
137  attrsOf :: a -> Attrs
138
139-- equality induced by attribution (EXPORTED)
140--
141eqOfAttrsOf           :: Attributed a => a -> a -> Bool
142eqOfAttrsOf obj1 obj2  = (attrsOf obj1) == (attrsOf obj2)
143
144-- position induced by attribution (EXPORTED)
145--
146posOfAttrsOf :: Attributed a => a -> Position
147posOfAttrsOf  = posOf . attrsOf
148
149
150-- attribute identifier creation
151-- -----------------------------
152
153-- Given only a source position, create a new attribute identifier (EXPORTED)
154--
155newAttrsOnlyPos     :: Position -> Attrs
156newAttrsOnlyPos pos  = OnlyPos pos
157
158-- Given a source position and a unique name, create a new attribute
159-- identifier (EXPORTED)
160--
161newAttrs          :: Position -> Name -> Attrs
162newAttrs pos name  = Attrs pos name
163
164
165-- attribute tables and operations on them
166-- ---------------------------------------
167
168-- the type class `Attr' determines which types may be used as attributes
169-- (EXPORTED)
170--
171--  * such types have to provide values representing an undefined and a don't
172--   care state, together with two functions to test for these values
173--
174--  * an attribute in an attribute table is initially set to `undef' (before
175--   some value is assigned to it)
176--
177--  * an attribute with value `dontCare' participated in an already detected
178--   error, it's value may not be used for further computations in order to
179--   avoid error avalanches
180--
181class Attr a where
182  undef      :: a
183  isUndef    :: a -> Bool
184  dontCare   :: a
185  isDontCare :: a -> Bool
186  undef       = interr "Attributes: Undefined `undef' method in `Attr' class!"
187  isUndef     = interr "Attributes: Undefined `isUndef' method in `Attr' \
188                       \class!"
189  dontCare    = interr "Attributes: Undefined `dontCare' method in `Attr' \
190                       \class!"
191  isDontCare  = interr "Attributes: Undefined `isDontCare' method in `Attr' \
192                       \class!"
193
194-- attribute tables map attribute identifiers to attribute values
195-- (EXPORTED ABSTRACT)
196--
197--  * the attributes within a table can be soft or frozen, the former may by be
198--   updated, but the latter can not be changed
199--
200--  * the attributes in a frozen table are stored in an array for fast
201--   lookup; consequently, the attribute identifiers must be *dense*
202--
203--  * the table description string is used to emit better error messages (for
204--   internal errors)
205--
206data Attr a =>
207     AttrTable a = -- for all attribute identifiers not contained in the
208                   -- finite map the value is `undef'
209                   --
210                   SoftTable (Map Name a)   -- updated attr.s
211                             String               -- desc of the table
212
213                   -- the array contains `undef' attributes for the undefined
214                   -- attributes; for all attribute identifiers outside the
215                   -- bounds, the value is also `undef';
216                   --
217                 | FrozenTable (Array Name a)     -- attribute values
218                               String             -- desc of the table
219
220
221
222-- create an attribute table, where all attributes are `undef' (EXPORTED)
223--
224-- the description string is used to identify the table in error messages
225-- (internal errors); a table is initially soft
226--
227newAttrTable      :: Attr a => String -> AttrTable a
228newAttrTable desc  = SoftTable Map.empty desc
229
230-- get the value of an attribute from the given attribute table (EXPORTED)
231--
232getAttr                      :: Attr a => AttrTable a -> Attrs -> a
233getAttr at (OnlyPos pos    )  = onlyPosErr "getAttr" at pos
234getAttr at (Attrs   _   aid)  =
235  case at of
236    (SoftTable   fm  _) -> Map.findWithDefault undef aid fm
237    (FrozenTable arr _) -> let (lbd, ubd) = bounds arr
238                           in
239                           if (aid < lbd || aid > ubd) then undef else arr!aid
240
241-- set the value of an, up to now, undefined attribute from the given
242-- attribute table (EXPORTED)
243--
244setAttr :: Attr a => AttrTable a -> Attrs -> a -> AttrTable a
245setAttr at (OnlyPos pos    ) av = onlyPosErr "setAttr" at pos
246setAttr at (Attrs   pos aid) av =
247  case at of
248    (SoftTable fm desc) -> assert (isUndef (Map.findWithDefault undef aid fm)) $
249                             SoftTable (Map.insert aid av fm) desc
250    (FrozenTable arr _) -> interr frozenErr
251  where
252    frozenErr     = "Attributes.setAttr: Tried to write frozen attribute in\n"
253                    ++ errLoc at pos
254
255-- update the value of an attribute from the given attribute table (EXPORTED)
256--
257updAttr :: Attr a => AttrTable a -> Attrs -> a -> AttrTable a
258updAttr at (OnlyPos pos    ) av = onlyPosErr "updAttr" at pos
259updAttr at (Attrs   pos aid) av =
260  case at of
261    (SoftTable   fm  desc) -> SoftTable (Map.insert aid av fm) desc
262    (FrozenTable arr _)    -> interr $ "Attributes.updAttr: Tried to\
263                                       \ update frozen attribute in\n"
264                                       ++ errLoc at pos
265
266-- copy the value of an attribute to another one (EXPORTED)
267--
268--  * undefined attributes are not copied, to avoid filling the table
269--
270copyAttr :: Attr a => AttrTable a -> Attrs -> Attrs -> AttrTable a
271copyAttr at ats ats'
272  | isUndef av = assert (isUndef (getAttr at ats'))
273                   at
274  | otherwise  = updAttr at ats' av
275  where
276    av = getAttr at ats
277
278-- auxiliary functions for error messages
279--
280onlyPosErr                :: Attr a => String -> AttrTable a -> Position -> b
281onlyPosErr fctName at pos  =
282  interr $ "Attributes." ++ fctName ++ ": No attribute identifier in\n"
283           ++ errLoc at pos
284--
285errLoc        :: Attr a => AttrTable a -> Position -> String
286errLoc at pos  = "  table `" ++ tableDesc at ++ "' for construct at\n\
287                 \  position " ++ show pos ++ "!"
288  where
289    tableDesc (SoftTable   _ desc) = desc
290    tableDesc (FrozenTable _ desc) = desc
291
292-- freeze a soft table; afterwards no more changes are possible until the
293-- table is softened again (EXPORTED)
294--
295freezeAttrTable                        :: Attr a => AttrTable a -> AttrTable a
296freezeAttrTable (SoftTable   fm  desc)  =
297  let contents = Map.toList fm
298      keys     = map fst contents
299      lbd      = minimum keys
300      ubd      = maximum keys
301  in
302  assert (length keys < 1000 || (length . range) (lbd, ubd) > 3 * length keys)
303  (FrozenTable (array (lbd, ubd) contents) desc)
304freezeAttrTable (FrozenTable arr desc)  =
305  interr ("Attributes.freezeAttrTable: Attempt to freeze the already frozen\n\
306          \  table `" ++ desc ++ "'!")
307
308-- soften a frozen table; afterwards changes are possible until the
309-- table is frozen again (EXPORTED)
310--
311softenAttrTable                        :: Attr a => AttrTable a -> AttrTable a
312softenAttrTable (SoftTable   fm  desc)  =
313  interr ("Attributes.softenAttrTable: Attempt to soften the already \
314          \softened\n  table `" ++ desc ++ "'!")
315softenAttrTable (FrozenTable arr desc)  =
316  SoftTable (Map.fromList . assocs $ arr) desc
317
318
319-- standard attributes
320-- -------------------
321
322-- standard attribute variants (EXPORTED)
323--
324data StdAttr a = UndefStdAttr
325               | DontCareStdAttr
326               | JustStdAttr a
327
328instance Attr (StdAttr a) where
329  undef = UndefStdAttr
330
331  isUndef UndefStdAttr = True
332  isUndef _            = False
333
334  dontCare = DontCareStdAttr
335
336  isDontCare DontCareStdAttr = True
337  isDontCare _               = False
338
339-- get an attribute value from a standard attribute table (EXPORTED)
340--
341--  * if the attribute can be "don't care", this should be checked before
342--   calling this function (using `isDontCareStdAttr')
343--
344getStdAttr         :: AttrTable (StdAttr a) -> Attrs -> a
345getStdAttr atab at  = getStdAttrDft atab at err
346  where
347    err = interr $ "Attributes.getStdAttr: Don't care in\n"
348                   ++ errLoc atab (posOf at)
349
350-- get an attribute value from a standard attribute table, where a default is
351-- substituted if the table is don't care (EXPORTED)
352--
353getStdAttrDft             :: AttrTable (StdAttr a) -> Attrs -> a -> a
354getStdAttrDft atab at dft  =
355  case getAttr atab at of
356    DontCareStdAttr -> dft
357    JustStdAttr av  -> av
358    UndefStdAttr    -> interr $ "Attributes.getStdAttrDft: Undefined in\n"
359                                ++ errLoc atab (posOf at)
360
361-- check if the attribue value is marked as "don't care" (EXPORTED)
362--
363isDontCareStdAttr         :: AttrTable (StdAttr a) -> Attrs -> Bool
364isDontCareStdAttr atab at  = isDontCare (getAttr atab at)
365
366-- check if the attribue value is still undefined (EXPORTED)
367--
368--  * we also regard "don't care" attributes as undefined
369--
370isUndefStdAttr         :: AttrTable (StdAttr a) -> Attrs -> Bool
371isUndefStdAttr atab at  = isUndef (getAttr atab at)
372
373-- set an attribute value in a standard attribute table (EXPORTED)
374--
375setStdAttr :: AttrTable (StdAttr a) -> Attrs -> a -> AttrTable (StdAttr a)
376setStdAttr atab at av = setAttr atab at (JustStdAttr av)
377
378-- update an attribute value in a standard attribute table (EXPORTED)
379--
380updStdAttr :: AttrTable (StdAttr a) -> Attrs -> a -> AttrTable (StdAttr a)
381updStdAttr atab at av = updAttr atab at (JustStdAttr av)
382
383
384-- generic attribute table access (EXPORTED)
385-- ------------------------------
386
387getGenAttr         :: (Attr a, Attributed obj) => AttrTable a -> obj -> a
388getGenAttr atab at  = getAttr atab (attrsOf at)
389
390setGenAttr            :: (Attr a, Attributed obj)
391                      => AttrTable a -> obj -> a -> AttrTable a
392setGenAttr atab at av  = setAttr atab (attrsOf at) av
393
394updGenAttr            :: (Attr a, Attributed obj)
395                      => AttrTable a -> obj -> a -> AttrTable a
396updGenAttr atab at av  = updAttr atab (attrsOf at) av
397
398
399{-! for Attrs derive : GhcBinary !-}
400{-! for AttrTable derive : GhcBinary !-}
401{-* Generated by DrIFT : Look, but Don't Touch. *-}
402instance Binary Attrs where
403    put_ bh (OnlyPos aa) = do
404            putByte bh 0
405            put_ bh aa
406    put_ bh (Attrs ab ac) = do
407            putByte bh 1
408            put_ bh ab
409            put_ bh ac
410    get bh = do
411            h <- getByte bh
412            case h of
413              0 -> do
414                    aa <- get bh
415                    return (OnlyPos aa)
416              1 -> do
417                    ab <- get bh
418                    ac <- get bh
419                    return (Attrs ab ac)
420
421instance (Binary a, Attr a) => Binary (AttrTable a) where
422    put_ bh (SoftTable aa ab) = do
423            putByte bh 0
424            put_ bh aa
425            put_ bh ab
426    put_ bh (FrozenTable ac ad) = do
427            putByte bh 1
428            put_ bh ac
429            put_ bh ad
430    get bh = do
431            h <- getByte bh
432            case h of
433              0 -> do
434                    aa <- get bh
435                    ab <- get bh
436                    return (SoftTable aa ab)
437              1 -> do
438                    ac <- get bh
439                    ad <- get bh
440                    return (FrozenTable ac ad)
441