--------------------------------------------------
-- Copyright 1994 by Peter Thiemann
-- $Log: EbnfLayout.hs,v $
-- Revision 1.1 2004/08/05 11:11:57 malcolm
-- Add a regression testsuite for the nhc98 compiler. It isn't very good,
-- but it is better than nothing. I've been using it for about four years
-- on nightly builds, so it's about time it entered the repository! It
-- includes a slightly altered version of the nofib suite.
-- Instructions are in the README.
--
-- Revision 1.2 1996/07/26 21:21:58 partain
-- Final changes for 2.01
--
-- Revision 1.1 1996/01/08 20:02:34 partain
-- Initial revision
--
-- Revision 1.4 1994/03/15 15:34:53 thiemann
-- added full color support, XColorDB based
--
-- Revision 1.3 1994/02/18 11:59:29 thiemann
-- save state before adding "withTentacle"
--
--Revision 1.2 1993/08/31 12:31:32 thiemann
--reflect changes in type FONT
--
--Revision 1.1 1993/08/17 12:34:29 thiemann
--Initial revision
--
-- $Locker: $
--------------------------------------------------
module EbnfLayout where
import AbstractSyntax
import Color
import Fonts (FONT, stringWidth, stringHeight, fontDescender)
import Info
import List--1.3
-- all arithmetic is done in 1/100 pt
-- tFont, ntFont :: Font
-- arrowSize, lineWidth, fatLineWidth, borderDistY, borderDistX :: Int
-- borderDistX = 500
-- borderDistY = 500
-- lineWidth = 20
-- fatLineWidth = 200
-- arrowSize = 300
-- ntFont = ("Times-Roman", 10)
-- tFont = ("Times-Roman", 10)
makePictureLayout :: INFO -> Production -> Container
makePictureLayout
(borderDistX, borderDistY, lineWidth, fatLineWidth, arrowSize, ntFont, tFont,
(ntColor, tColor, lineColor, fatLineColor))
prod
= makePicture 0 0 1 prod
where
mkNonTerminal :: String -> Int -> Int -> Container
mkNonTerminal str rx ry = (rx, ry, width, height, 0, AString ntColor ntFont str)
where
width = stringWidth ntFont str
height = stringHeight ntFont str
mkTerminal :: String -> Int -> Int -> Container
mkTerminal str rx ry = (rx, ry, width, height, 0, AString tColor tFont str)
where
width = stringWidth tFont str
height = stringHeight tFont str
mkBox :: Int -> Int -> Int -> Int -> Int -> Container -> Container
mkBox rx ry width height inOutY content
= (rx, ry, width, height, inOutY, ABox fatLineColor False content)
mkRoundBox :: Int -> Int -> Int -> Int -> Int -> Container -> Container
mkRoundBox rx ry width height inOutY content
= (rx, ry, width, height, inOutY, ABox fatLineColor True content)
mkLine :: Int -> Int -> Int -> Int -> Container
mkLine rx ry w h = (rx, ry, w, h, 0, Aline lineColor)
mkArrow :: Int -> Int -> Int -> Container
mkArrow rx ry dir = (rx, ry, 0, 0, 0, Arrow lineColor (dir*arrowSize))
mkTurn :: Int -> Int -> Int -> Int -> TDirection -> Container
mkTurn rx ry w h t = (rx, ry, w, h, 0, ATurn lineColor t)
------------------------------------------------------------------------
withTentacle :: Int -> Int -> Int -> Production -> Container
withTentacle rx ry direction prod =
(rx, ry, width, height, inOutY, AComposite [contents, theLine, theArrow])
where (_, _, width1, height, inOutY, _) = contents
contents = makePicture rx1 0 direction prod
width = width1 + borderDistX
rx1 | direction > 0 = 0
| otherwise = borderDistX
theLine | direction > 0 = mkLine width1 inOutY borderDistX 0
| otherwise = mkLine 0 inOutY borderDistX 0
theArrow | direction > 0 = mkArrow width inOutY direction
| otherwise = mkArrow 0 inOutY direction
makePicture :: Int -> Int -> Int -> Production -> Container
makePicture rx ry direction (ProdProduction ntName ntAliases prod) =
(rx, ry, width, height, 0, AComposite ([content1, content2]++glue))
where (_, _, width1, height1, inOutY1, _) = content1
content1 = withTentacle rx1 ry1 direction prod
content2@(_,_, width2, height2,_,_) = mkNonTerminal str rx2 ry2
rx1 = 2*borderDistX
ry1 = fatLineWidth `div` 2
rx2 = 0
ry2 = ry1 + height1 + distance - fontDescender ntFont
distance = 2*borderDistY
width = 2*borderDistX + max width1 width2
height = height1 +fatLineWidth + height2 + distance
glue = [
mkLine 0 (ry1 + inOutY1) (2*borderDistX) 0,
mkArrow rx1 (ry1 + inOutY1) direction]
str = case ntAliases of
[] -> ntName
newName:_ -> newName
makePicture rx ry direction (ProdTerm [prod]) =
makePicture rx ry direction prod
makePicture rx ry direction (ProdTerm prods) =
(rx, ry, width, height, inOutY, AComposite (newcontents ++ glue))
where newcontents = zip6 rxs rys widths heights inOutYs gobjs
(_, _, widths, heights, inOutYs, gobjs) = unzip6 contents
ncontents = length prods
-- sadly enough it's not possible to take rxs and rys in place of the fakes!
fakes = take ncontents (repeat 0)
contents = zipWith4 makePicture fakes fakes directions prods
height = sum heights + (ncontents-1) * borderDistY
maxwidth = maximum widths
width = maxwidth + 4 * borderDistX
rxs | direction > 0 = take ncontents (repeat (2 * borderDistX))
| otherwise = map ((+ 2*borderDistX) . (maxwidth -)) widths
rys = tail (scanr f 0 heights) where f h q = h + q + borderDistY
directions = take ncontents (repeat direction)
entries = zipWith (+) rys inOutYs -- frame relative Y positions of entries
firstEntry = entries!!0
lastEntry = entries!!(ncontents-1)
middleEntries = init (tail entries)
inOutY = (firstEntry + lastEntry) `div` 2
inOutDiff = firstEntry - lastEntry - 2*borderDistY
glue = fixedglue ++ variableglue
fixedglue = [
mkLine 0 inOutY borderDistX 0,
mkLine (width-borderDistX) inOutY borderDistX 0,
mkTurn borderDistX (firstEntry - borderDistY) borderDistX borderDistY SE,
mkTurn borderDistX lastEntry borderDistX borderDistY NE,
mkLine borderDistX (lastEntry + borderDistY) 0 inOutDiff,
mkTurn (width-2*borderDistX) (firstEntry - borderDistY) borderDistX borderDistY SW,
mkTurn (width-2*borderDistX) lastEntry borderDistX borderDistY WN,
mkLine (width-borderDistX) (lastEntry + borderDistY) 0 inOutDiff] ++
map f middleEntries ++
map g middleEntries
where f y = mkLine borderDistX y borderDistX 0
g y = mkLine (width-2*borderDistX) y borderDistX 0
variableglue | direction > 0 = zipWith g widths entries
| otherwise = zipWith h widths entries
where g w y = mkLine (2*borderDistX + w) y (maxwidth - w) 0
h w y = mkLine (2*borderDistX) y (maxwidth - w) 0
obsoleteglue =
map (f (2*borderDistX)) entries
where f x y = mkArrow x y direction
{- the following works for two terms, both directions
makePicture rx ry direction (ProdTerm [prod1, prod2])
| direction > 0 =
let (_, _, width1, height1, inOutY1, _) = content1
content1 = makePicture rx1 ry1 direction prod1
(_, _, width2, height2, inOutY2, _) = content2
content2 = makePicture rx2 ry2 direction prod2
rx1 = 2*borderDistX
rx2 = 2*borderDistX
ry2 = 0
ry1 = height2 + borderDistY
maxwidth = max width1 width2
width = 4*borderDistX + maxwidth
height = height2 + borderDistY + height1
inOutY = (inOutY2 + ry1 + inOutY1) `div` 2
inOutDiff = ry1 + inOutY1 - inOutY2 - 2*borderDistY
glue = [
mkLine 0 inOutY borderDistX 0,
mkLine (width - borderDistX) inOutY borderDistX 0,
mkLine borderDistX (ry2 + inOutY2 + borderDistY) 0 inOutDiff,
mkLine (width - borderDistX) (ry2 + inOutY2 + borderDistY) 0 inOutDiff,
mkTurn borderDistX (ry1 + inOutY1 - borderDistX) borderDistX borderDistY SE,
mkTurn borderDistX inOutY2 borderDistX borderDistY NE,
mkTurn (width - 2*borderDistX) (ry1 + inOutY1 - borderDistX) borderDistX borderDistY SW,
mkTurn (width - 2*borderDistX) inOutY2 borderDistX borderDistY WN,
mkLine (rx1 + width1) (ry1 + inOutY1) (maxwidth - width1) 0,
mkLine (rx2 + width2) (ry2 + inOutY2) (maxwidth - width2) 0]
in (rx, ry, width, height, inOutY, AComposite ([content1,content2]++glue))
| otherwise =
let (_, _, width1, height1, inOutY1, _) = content1
content1 = makePicture rx1 ry1 direction prod1
(_, _, width2, height2, inOutY2, _) = content2
content2 = makePicture rx2 ry2 direction prod2
maxwidth = max width1 width2
width = 4*borderDistX + maxwidth
height = height2 + borderDistY + height1
inOutY = (inOutY2 + ry1 + inOutY1) `div` 2
inOutDiff = ry1 + inOutY1 - inOutY2 - 2*borderDistY
rx1 = 2*borderDistX + (maxwidth - width1)
rx2 = 2*borderDistX + (maxwidth - width2)
ry2 = 0
ry1 = height2 + borderDistY
glue = [
mkLine 0 inOutY borderDistX 0,
mkLine (width - borderDistX) inOutY borderDistX 0,
mkLine borderDistX (ry2 + inOutY2 + borderDistY) 0 inOutDiff,
mkLine (width - borderDistX) (ry2 + inOutY2 + borderDistY) 0 inOutDiff,
mkTurn borderDistX (ry1 + inOutY1 - borderDistX) borderDistX borderDistY SE,
mkTurn borderDistX inOutY2 borderDistX borderDistY NE,
mkTurn (width - 2*borderDistX) (ry1 + inOutY1 - borderDistX) borderDistX borderDistY SW,
mkTurn (width - 2*borderDistX) inOutY2 borderDistX borderDistY WN,
mkLine (2*borderDistX) (ry1 + inOutY1) (maxwidth - width1) 0,
mkLine (2*borderDistX) (ry2 + inOutY2) (maxwidth - width2) 0]
in (rx, ry, width, height, inOutY, AComposite ([content1,content2]++glue))
-}
makePicture rx ry direction (ProdFactor [prod]) =
makePicture rx ry direction prod
{-
makePicture rx ry direction (ProdFactor prods) =
(rx, ry, width, height, inOutY, AComposite (glue++contents))
where (_, _, widths, heights, inOutYs, gobjs) = unzip6 contents
contents = zipWith4 makePicture rxs rys directions prods
ncontents = length prods
aboves = zipWith (-) heights inOutYs
maxIO = maximum inOutYs
height = maxIO + maximum aboves
width = sum widths + (ncontents-1)*borderDistX
inOutY = maxIO
rxs
-- = take ncontents [0, 20*borderDistX .. ]
| direction > 0 = init (scanl f 0 widths)
| otherwise = tail (scanr f 0 widths)
where f q w = q + w + borderDistX
rys =
take ncontents (repeat 0)
-- map (inOutY -) inOutYs
directions = take ncontents (repeat direction)
glue | direction > 0 = map f (tail rxs)
| otherwise = map f (init rxs)
where f x = mkLine (x-borderDistX) inOutY borderDistX 0
-}
makePicture rx ry direction (ProdFactor [prod1,prod2])
| direction > 0 =
let (_, _, width1, height1, inOutY1, _) = content1
content1 = withTentacle rx1 ry1 direction prod1
(_, _, width2, height2, inOutY2, _) = content2
content2 = makePicture rx2 ry2 direction prod2
rx1 = 0
rx2 = width1
width = width1 + width2
inOutY = max inOutY1 inOutY2
ry1 = inOutY - inOutY1
ry2 = inOutY - inOutY2
height = inOutY + max (height1 - inOutY1) (height2 - inOutY2)
in (rx, ry, width, height, inOutY, AComposite ([content1,content2]))
| otherwise =
let (_, _, width1, height1, inOutY1, _) = content1
content1 = withTentacle rx1 ry1 direction prod1
(_, _, width2, height2, inOutY2, _) = content2
content2 = makePicture rx2 ry2 direction prod2
rx2 = 0
rx1 = width2
width = width1 + width2
inOutY = max inOutY1 inOutY2
ry1 = inOutY - inOutY1
ry2 = inOutY - inOutY2
height = inOutY + max (height1 - inOutY1) (height2 - inOutY2)
in (rx, ry, width, height, inOutY, AComposite ([content1,content2]))
makePicture rx ry direction (ProdFactor (prod:prods)) =
makePicture rx ry direction (ProdFactor [prod, ProdFactor prods])
-- this is a ghastly hack!
makePicture rx ry direction (ProdNonterminal str) =
mkBox rx ry width height inOutY content
where content@(_,_,width', height',_,_) = mkNonTerminal str rx' ry'
width = width' + 2*borderDistX + 2*fatLineWidth
height = height' + borderDistY + 2*fatLineWidth
rx' = fatLineWidth + borderDistX
ry' = fatLineWidth + borderDistY `div` 2 - fontDescender ntFont
inOutY = height `div` 2
makePicture rx ry direction (ProdTerminal str) =
mkRoundBox rx ry width height inOutY content
where content@(_,_,width', height',_,_) = mkTerminal str rx' ry'
width = width' + 2*borderDistX + 2*fatLineWidth
height = height' + borderDistY + 2*fatLineWidth
rx' = fatLineWidth + borderDistX
ry' = fatLineWidth + borderDistY `div` 2 - fontDescender tFont
inOutY = height `div` 2
makePicture rx ry direction (ProdOption prod) =
(rx, ry, width, height, inOutY, AComposite (content:glue))
where (_, _, width', height', inOutY', gobj) = content
content = makePicture rx' ry' direction prod
width = width' + 6*borderDistX
height = height' + borderDistY
rx' = 3*borderDistX
ry' = borderDistY
inOutY = 0
glue = variableglue ++ fixedglue
fixedglue = [
mkLine 0 0 width 0,
mkTurn 0 0 borderDistX bby WN,
mkTurn borderDistX (inOutY'+borderDistY-bby) borderDistX bby SE,
mkLine (2*borderDistX) (ry'+inOutY') borderDistX 0,
mkTurn (width-borderDistX) 0 borderDistX bby NE,
mkTurn (width-2*borderDistX) (inOutY'+borderDistY-bby) borderDistX bby SW,
mkLine (width-3*borderDistX) (ry'+inOutY') borderDistX 0,
mkLine borderDistX bby 0 (inOutY'+borderDistY-2*bby),
mkLine (width-borderDistX) bby 0 (inOutY'+borderDistY-2*bby)]
where bby = min borderDistY ((inOutY'+borderDistY) `div` 2)
variableglue
| direction > 0 = [mkArrow (3*borderDistX) (ry'+inOutY') direction]
| otherwise = [mkArrow (width-3*borderDistX) (ry'+inOutY'+borderDistY) direction]
makePicture rx ry direction (ProdRepeat prod) =
(rx, ry, width, height, inOutY, AComposite (content:glue))
where (_, _, width', height', inOutY', gobj) = content
content = makePicture rx' ry' (-direction) prod
width = width' + 4*borderDistX
height = height' + borderDistY
rx' = 2*borderDistX
ry' = borderDistY
inOutY = 0
glue = variableglue ++ fixedglue
fixedglue = [
mkLine 0 0 width 0,
mkTurn borderDistX 0 borderDistX bby NE,
mkTurn borderDistX (inOutY'+borderDistY-bby) borderDistX bby SE,
mkTurn (width-2*borderDistX) 0 borderDistX bby WN,
mkTurn (width-2*borderDistX) (inOutY'+borderDistY-bby) borderDistX bby SW,
mkLine borderDistX bby 0 (inOutY'+borderDistY-2*bby),
mkLine (width-borderDistX) bby 0 (inOutY'+borderDistY-2*bby)]
where bby = min borderDistY ((inOutY'+borderDistY) `div` 2)
variableglue
| direction < 0 = [mkArrow (2*borderDistX) (inOutY'+borderDistY) (-direction)]
| otherwise = [mkArrow (width-2*borderDistX) (inOutY'+borderDistY) (-direction)]
makePicture rx ry direction (ProdRepeat1 prod) =
(rx, ry, width, height, inOutY, AComposite (content:glue))
where (_, _, width', height', inOutY', gobj) = content
content = makePicture rx' ry' (direction) prod
width = width' + 4*borderDistX
height = height' + borderDistY
rx' = 2*borderDistX
ry' = 0
inOutY = inOutY'
glue = [
mkLine 0 inOutY rx' 0,
mkLine (rx'+width') inOutY rx' 0,
mkTurn borderDistX inOutY borderDistX borderDistY NE,
mkTurn borderDistX (height-borderDistY) borderDistX borderDistY SE,
mkTurn (width-rx') inOutY borderDistX borderDistY WN,
mkTurn (width-rx') (height-borderDistY) borderDistX borderDistY SW,
mkLine borderDistX (inOutY+borderDistY) 0 (height'-inOutY'-borderDistY),
mkLine (width-borderDistX) (inOutY+borderDistY) 0 (height'-inOutY'-borderDistY),
mkLine rx' height width' 0,
mkArrow (rx'+width' `div` 2) height (-direction)]
makePicture rx ry direction (ProdRepeatWithAtom prod1 prod2) =
(rx, ry, width, height, inOutY, AComposite (content1:content2:glue))
where (_, _, width1, height1, inOutY1, _) = content1
(_, _, width2, height2, inOutY2, _) = content2
content1 = makePicture rx1 ry1 direction prod1
content2 = makePicture rx2 ry2 (-direction) prod2
maxwidth = max width1 width2
width = maxwidth + 4*borderDistX
height = height1 + height2 + borderDistY
adjx1 = (maxwidth - width1) `div` 2
rx1 = 2*borderDistX + adjx1
ry1 = 0
adjx2 = (maxwidth - width2) `div` 2
rx2 = 2*borderDistX + adjx2
ry2 = height1 + borderDistY
inOutY = inOutY1
glue = variableglue ++ fixedglue
fixedglue = [
mkLine 0 inOutY rx1 0,
mkLine (rx1 + width1) inOutY rx1 0,
mkLine (2*borderDistX) (ry2+inOutY2) adjx2 0,
mkLine (2*borderDistX + adjx2 + width2) (ry2+inOutY2) adjx2 0,
mkTurn borderDistX inOutY borderDistX borderDistY NE,
mkTurn borderDistX (ry2+inOutY2-borderDistY) borderDistX borderDistY SE,
mkTurn (rx1+width1+adjx1) inOutY borderDistX borderDistY WN,
mkTurn (rx1+width1+adjx1) (ry2+inOutY2-borderDistY) borderDistX borderDistY SW,
mkLine borderDistX (inOutY+borderDistY) 0 (height1-inOutY1 + inOutY2 - borderDistY),
mkLine (rx1+width1+adjx1+borderDistX) (inOutY+borderDistY) 0 (height1-inOutY1 + inOutY2 - borderDistY)]
variableglue
| direction > 0 = [mkArrow (2*borderDistX + adjx2 + width2) (ry2+inOutY2) (-direction)]
| otherwise = [mkArrow (2*borderDistX + adjx2) (ry2+inOutY2) (-direction)]
|