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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
|
\begin{Code}
PRAGMA strictdata
PRAGMA optimize
PRAGMA bangpats
PRAGMA strictwrap
INCLUDE "Interfaces.ag"
imports
{
import Interfaces
import CodeSyntax
import GrammarInfo
import qualified Data.Sequence as Seq
import Data.Sequence(Seq)
import qualified Data.Map as Map
import Data.Map(Map)
import Data.Tree(Tree(Node), Forest)
import Data.Graph(Graph, dfs, edges, buildG, transposeG)
import Data.Maybe (fromJust)
import Data.List (partition,transpose,(\\),nub,findIndex)
import Data.Array ((!),inRange,bounds,assocs)
import Data.Foldable(toList)
}
\end{Code}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Visit sub-sequence-graph}
Visit sub-sequences can be generated from the |Tdp| by a topological
sort. To that end we add vertices to |Tdp|. For each production, for
each child, for each visit to that child, we add a vertex $v$.
We add the following edges:
\begin{enumerate}
\item From the inherited attributes passed to the visit to $v$,
because these attributes need to be computed before visiting $v$.
\item From the synthesized attributes computed by the visit to
$v$, because a visit to $v$ computes these attributes.
\item From the previous visit to $v$, because we can only visit
$c$ for the $i$-th time if we have visited it the $(i-1)$-th time.
\end{enumerate}
Now we can define a visit sub-sequence as a list of vertices:
\begin{Code}
{
type VisitSS = [Vertex]
}
\end{Code}
We define a function that generates the visit-subsequences-graph and a
description of the newly added vertices. We do this using an attribute
grammar. The visit subsequences graph has transposed edges, so we can
use |topSort'|.
\begin{Code}
ATTR IRoot [ tdp : Graph | | ]
SEM IRoot
| IRoot loc.newedges = toList @inters.newedges
loc.visitssGraph = let graph = buildG (0,@inters.v-1) es
es = @newedges ++ edges @lhs.tdp
in transposeG graph
\end{Code}
As we will need to look up information, we pass |info| down. An
attribute v stores a fresh vertex. We start counting from the hightest
vertex in |tdp|.
\begin{Code}
ATTR Interfaces Interface Segments Segment [ | v : Vertex | ]
ATTR IRoot Interfaces Interface Segments Segment [ info : Info | | ]
SEM IRoot
| IRoot inters.v = snd (bounds @lhs.tdp) + 1
\end{Code}
The actual generation of edges takes place in |Segment|. We group the
attribute occurrences. |isEqualField| checks are at the same position
(either lhs of the same child).
\begin{Code}
{
gather :: Info -> [Vertex] -> [[Vertex]]
gather info = eqClasses comp
where comp a b = isEqualField (ruleTable info ! a) (ruleTable info ! b)
}
\end{Code}
When we do this for right-hand side occurrences of the inherited and
syntesized attributes of a |Segment|, we find the new vertices.
\begin{Code}
SEM Segment
| Segment loc.look : {Vertex -> CRule}
loc.look = \a -> ruleTable @lhs.info ! a
loc.occurAs : {(CRule -> Bool) -> [Vertex] -> [Vertex]}
loc.occurAs = \p us -> [ a | u <- us
, a <- tdsToTdp @lhs.info ! u
, p (@look a)]
loc.groups : {[([Vertex],[Vertex])]}
loc.groups = let group as = gather @lhs.info (@occurAs isRhs as)
in map (partition (isInh . @look)) (group (@inh ++ @syn))
loc.v : {Int}
loc.v = @lhs.v + length @groups
loc.newvertices = [@lhs.v .. @loc.v-1]
\end{Code}
A description of the new vertices van be found by looking up the field
of an attribute occurrence
\begin{Code}
ATTR Interfaces Interface Segments Segment
[ visitDescr : {Map Vertex ChildVisit} | | ]
SEM IRoot
| IRoot inters.visitDescr = Map.fromList @descr
ATTR Interfaces Interface Segments Segment
[ | | newedges USE {Seq.><} {Seq.empty} : {Seq Edge }
descr USE {Seq.><} {Seq.empty} : {Seq (Vertex,ChildVisit)} ]
SEM Segment
| Segment lhs.descr = Seq.fromList $ zipWith (cv @look @lhs.n) @newvertices @groups {-$-}
{
-- Only non-empty syn will ever be forced, because visits with empty syn are never performed
-- Right hand side synthesized attributes always have a field
cv :: (Vertex -> CRule) -> Int -> Vertex -> ([Vertex],[Vertex]) -> (Vertex,ChildVisit)
cv look n v (inh,syn) = let fld = getField (look (head syn))
rnt = fromJust (getRhsNt (look (head syn)))
d = ChildVisit fld rnt n inh syn
in (v,d)
}
\end{Code}
\begin{Code}
SEM IRoot
| IRoot loc.descr = toList @inters.descr
\end{Code}
The edges between attributes occurrences and their corresponding
visits can be found as follows:
\begin{Code}
SEM Segment
| Segment loc.attredges = concat (zipWith ed @newvertices @groups)
{
ed :: Vertex -> ([Vertex], [Vertex]) -> [(Vertex, Vertex)]
ed v (inh,syn) = map (\i -> (i,v)) inh ++ map (\s -> (v,s)) syn
}
\end{Code}
For edges between visits we simpy |zip| the current vertices with the
next ones.
\begin{Code}
ATTR Segment [ nextNewvertices : {[Vertex]} | | newvertices : {[Vertex]} ]
ATTR Segments [ | | newvertices : {[Vertex]} ]
SEM Segments
| Cons hd.nextNewvertices = @tl.newvertices
lhs.newvertices = @hd.newvertices
| Nil lhs.newvertices = []
SEM Segment
| Segment loc.visitedges = zip @newvertices @lhs.nextNewvertices
lhs.newedges = Seq.fromList @attredges Seq.>< Seq.fromList @visitedges
\end{Code}
The first visit to a child is passed to the first visit of the parent,
so we add edges for this, too.
\begin{Code}
ATTR Segments Segment [ | | groups : {[([Vertex],[Vertex])]} ]
SEM Segments
| Cons lhs.groups = @hd.groups
| Nil lhs.groups = []
SEM Interface
| Interface seg.v = @lhs.v
loc.v = @seg.v + length @seg.newvertices
lhs.v = @loc.v
loc.firstvisitvertices = [@seg.v .. @v-1]
loc.newedges = zip @firstvisitvertices @seg.newvertices
lhs.newedges = @seg.newedges Seq.>< Seq.fromList @newedges
loc.look : {Vertex -> CRule}
loc.look = \a -> ruleTable @lhs.info ! a
loc.descr = zipWith (cv @look (-1)) @firstvisitvertices @seg.groups
lhs.descr = @seg.descr Seq.>< Seq.fromList @descr
\end{Code}
The visit number can simply be counted
\begin{Code}
ATTR Segments Segment [ n : Int | | ]
SEM Interface
| Interface seg.n = 0
SEM Segments
| Cons tl.n = @lhs.n + 1
\end{Code}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Visit sub-sequences}
To compute the visit subsequences, we pass the visit-subsequence graph down
\begin{Code}
ATTR Interfaces Interface Segments Segment [ vssGraph : Graph | | ]
SEM IRoot
| IRoot inters.vssGraph = @visitssGraph
\end{Code}
Each segment computes subsequences for each production of the
nonterminal. We group the occurrences of the synthesized attributes,
and perform a topological sort on it. In the absence of synthesized
attributes, nothing needs to be computed, so the visit subsequence
is empty.
\begin{Code}
SEM Segment
| Segment loc.synOccur = gather @lhs.info (@occurAs isLhs @syn)
loc.vss = let hasCode' v | inRange (bounds (ruleTable @lhs.info)) v = getHasCode (ruleTable @lhs.info ! v)
| otherwise = True
in if null @syn
then replicate (length @lhs.cons) []
else map (filter hasCode' . topSort' @lhs.vssGraph) @synOccur
ATTR Segments Segment [ cons : {[ConstructorIdent]} | | ]
SEM Interface
| Interface seg.cons = @cons
\end{Code}
We adapt the topological sort to take a list of vertices to start
sorting.
\begin{Code}
{
postorder :: Tree a -> [a]
postorder (Node a ts) = postorderF ts ++ [a]
postorderF :: Forest a -> [a]
postorderF = concatMap postorder
postOrd :: Graph -> [Vertex] -> [Vertex]
postOrd g = postorderF . dfs g
topSort' :: Graph -> [Vertex] -> [Vertex]
topSort' g = postOrd g
}
\end{Code}
This gives us the subsequence required to compute the synthesized
attributes. However, a part of this subsequence has already been
computed in previous visits. We thread this part through. It starts
with all first visits to children.
\begin{Code}
ATTR Interfaces Interface [ prev : {[Vertex]} | | firstvisitvertices USE {++} {[]} : {[Vertex]} ]
SEM IRoot
| IRoot inters.prev = let terminals = [ v | (v,cr) <- assocs (ruleTable @lhs.info), not (getHasCode cr), isLocal cr ]
in @inters.firstvisitvertices ++ terminals
ATTR Segments Segment [ | prev : {[Vertex]} | ]
\end{Code}
and we remove this part from the subsequence
\begin{Code}
SEM Segment [ | | visitss : {[VisitSS]} ]
| Segment loc.visitss' = map (\\ @lhs.prev) @vss
loc.defined = let defines v = case Map.lookup v @lhs.visitDescr of
Nothing -> [v]
Just (ChildVisit _ _ _ inh _) -> v:inh
in concatMap (concatMap defines) @visitss
lhs.prev = @lhs.prev ++ @defined
\end{Code}
When more that one attribute is defined in the same rule, this rule is
repeated in the visit subsequence. We do not want this.
\begin{Code}
SEM Segment
| Segment loc.visitss : {[[Vertex]]}
loc.visitss = let rem' :: [(Identifier,Identifier,Maybe Type)] -> [Vertex] -> [Vertex]
rem' _ [] = []
rem' prev (v:vs)
| inRange (bounds table) v
= let cr = table ! v
addV = case findIndex cmp prev of
Just _ -> id
_ -> (v:)
cmp (fld,attr,tp) = getField cr == fld && getAttr cr == attr && sameNT (getType cr) tp
sameNT (Just (NT ntA _ _)) (Just (NT ntB _ _)) = ntA == ntB
sameNT _ _ = False
def = Map.elems (getDefines cr)
in addV (rem' (def ++ prev) vs)
| otherwise = v:rem' prev vs
table = ruleTable @lhs.info
in map (rem' []) @visitss'
\end{Code}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Intra-visit dependencies}
We ignore terminals, they need to be passed from the first visit up to
where they are needed. Intra-visit dependencies descibe what a visit
needs from its previous visits. The first visit does not have
intra-visit dependencies, because there are no previous visits. We add
an attribute that indicates whether it's the first visit.
\begin{Code}
ATTR Segment Segments [ isFirst : {Bool} | | ]
SEM Interface
| Interface seg.isFirst = True
SEM Segments
| Cons tl.isFirst = False
\end{Code}
We declare an attribute intravisit which gives the intra-visit
dependencies. We pass the intravisit of the next visit to this
one.
\begin{Code}
{
type IntraVisit = [Vertex]
}
ATTR Segment [ nextIntravisits : {[IntraVisit]} | | intravisits : {[IntraVisit]} ]
SEM Segments [ | | hdIntravisits : {[IntraVisit]} ]
| Cons hd.nextIntravisits = @tl.hdIntravisits
lhs.hdIntravisits = @hd.intravisits
| Nil lhs.hdIntravisits = repeat []
\end{Code}
The first visit does not have intra-visit dependencies. A later visit
need all attributes that it's subsequence depends on, and the
intra-visit dependecies of the next visit, except for those attributes
that are compted in this visit.
\begin{Code}
ATTR IRoot [ dpr : {[Edge]} | | ]
ATTR Interfaces Interface Segments Segment [ ddp : Graph | | ]
SEM IRoot
| IRoot inters.ddp = buildG (0,@inters.v-1) (map swap (@lhs.dpr ++ @newedges))
{
swap :: (a,b) -> (b,a)
swap (a,b) = (b,a)
}
ATTR Segments Segment [ fromLhs : {[Vertex]} | | ]
SEM Interface
| Interface seg.fromLhs = @lhs.prev
SEM Segments
| Cons hd.fromLhs = @lhs.fromLhs
tl.fromLhs = []
SEM Segment
| Segment loc.fromLhs = @occurAs isLhs @inh ++ @lhs.fromLhs
loc.computed = let computes v = case Map.lookup v @lhs.visitDescr of
Nothing -> Map.keys (getDefines (ruleTable @lhs.info ! v))
Just (ChildVisit _ _ _ _ syn) -> v:syn
in concatMap (concatMap computes) @visitss
loc.intravisits = zipWith @iv @visitss @lhs.nextIntravisits
loc.iv = \vs next ->
let needed = concatMap (@lhs.ddp !) vs
in nub (needed ++ next) \\ (@fromLhs ++ @computed)
\end{Code}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Result}
Our resulting datastructure is:
Now we pass the visit sub-sequences up. In |Interface|, |@seg.visitss|
gives us for each segment, for each production a subsequence. What we
want is for each production, for each visit a subsequence, which is
accomplished by |transpose|. The same is done for intravisits.
\begin{Code}
ATTR Interfaces Interface Segments Segment [ allInters : {CInterfaceMap} | | ]
SEM IRoot
| IRoot inters.allInters = @inters.inters
ATTR IRoot Interfaces [ | | inters : {CInterfaceMap}
visits : {CVisitsMap} ]
SEM Interfaces
| Cons lhs.inters = Map.insert @hd.nt @hd.inter @tl.inters
lhs.visits = Map.insert @hd.nt @hd.visits @tl.visits
| Nil lhs.inters = Map.empty
lhs.visits = Map.empty
SEM Interface [ | | nt : NontermIdent ]
| Interface lhs.nt = @nt
SEM Interface [ | | inter : CInterface
visits : {Map ConstructorIdent CVisits} ]
| Interface lhs.inter = CInterface @seg.segs
lhs.visits = Map.fromList (zip @cons (transpose @seg.cvisits))
SEM Segments [ | | segs : CSegments
cvisits USE {:} {[]} : {[[CVisit]]} ] -- For each visit, for each constructor the CVisit
| Cons lhs.segs = @hd.seg : @tl.segs
| Nil lhs.segs = []
SEM Segment [ | | seg : CSegment
cvisits : {[CVisit]} ] -- For this visit, for each constructor the CVisit
| Segment lhs.seg = -- A fake dependency fixes a type-3 cycle
if False then undefined @lhs.vssGraph @lhs.visitDescr @lhs.prev else CSegment @inhmap @synmap
loc.inhmap : {Map Identifier Type}
loc.synmap : {Map Identifier Type}
loc.(inhmap,synmap) = let makemap = Map.fromList . map findType
findType v = getNtaNameType (attrTable @lhs.info ! v)
in (makemap @inh,makemap @syn)
lhs.cvisits = let mkVisit vss intra = CVisit @inhmap @synmap (mkSequence vss) (mkSequence intra) True
mkSequence = map mkRule
mkRule v = case Map.lookup v @lhs.visitDescr of
Nothing -> ruleTable @lhs.info ! v
Just (ChildVisit name nt n _ _) -> ccv name nt n @lhs.allInters
in zipWith mkVisit @visitss @intravisits
{
ccv :: Identifier -> NontermIdent -> Int -> CInterfaceMap -> CRule
ccv name nt n table
= CChildVisit name nt n inh syn lst
where CInterface segs = Map.findWithDefault (error ("InterfacesRules::ccv::interfaces not in table for nt: " ++ show nt)) nt table
(seg:remain) = drop n segs
CSegment inh syn = seg
lst = null remain
}
\end{Code}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{EDP}
To find a type-3 cycle we need to know the dependencies that the
interfaces generate.
\begin{Code}
ATTR Interfaces Interface Segments Segment [ | | edp USE {Seq.><} {Seq.empty} : {Seq Edge} ]
SEM Segment
| Segment lhs.edp = Seq.fromList [(i,s) | i <- @inh, s <- @syn]
Seq.>< Seq.fromList [(s,i) | s <- @syn, i <- @lhs.nextInh ]
SEM IRoot [ | | edp : {[Edge]} ]
| IRoot lhs.edp = toList @inters.edp
SEM Segment [ nextInh : {[Vertex]} | | inh : {[Vertex]} ]
| Segment lhs.inh = @inh
SEM Segments [ | | firstInh : {[Vertex]} ]
| Cons hd.nextInh = @tl.firstInh
lhs.firstInh = @hd.inh
| Nil lhs.firstInh = []
\end{Code}
|