packages feed

cpsa-2.2.1: src/CPSA/Graph/LaTeXView.hs

-- Generates a text view of CPSA output as a LaTeX document.

-- Copyright (c) 2009 The MITRE Corporation
--
-- This program is free software: you can redistribute it and/or
-- modify it under the terms of the BSD License as published by the
-- University of California.

module CPSA.Graph.LaTeXView (latexView) where

import System.IO
import CPSA.Lib.CPSA (nats, SExpr, Pos)
import CPSA.Graph.Loader
import CPSA.Graph.Layout

type Printer a = Int -> Int -> SExpr a -> String

-- Main entry point
latexView :: Handle -> Int -> Printer Pos -> [SExpr Pos] ->
             Preskel -> State -> IO ()
latexView h margin pp cmts k s =
    do
      hPutStrLn h "\\usepackage[matrix,arrow,curve]{xy}"
      hPutStrLn h "\\begin{document}"
      hPutStrLn h "\\begin{verbatim}"
      mapM_ (writeItem h margin pp) cmts
      hPutStrLn h "\\end{verbatim}"
      writeLnPreskel h margin pp k s

-- Write each preskeleton
writeLnPreskel :: Handle -> Int -> Printer Pos -> Preskel -> State -> IO ()
writeLnPreskel h m pp k s =
    do
      case parent k of          -- Write protocol with first preskeleton
        Nothing ->
            do
              hPutStrLn h (showString "\n\\paragraph{Tree " $
                                      shows (label k) "}\n")
              hPutStrLn h "\n\\begin{verbatim}"
              writeItem h m pp (protSrc k)
              hPutStrLn h "\\end{verbatim}"
        Just _ -> return ()
      hPutStrLn h (showString "\n\\paragraph{Label " $ shows (label k) "}\n")
      hPutStrLn h (show (matrix k))
      hPutStrLn h "\n\\begin{verbatim}"
      writeItem h m pp (preskelSrc k)
      hPutStrLn h "\\end{verbatim}"
      n <- loadNext s
      case n of
        Nothing ->              -- EOF
            do
              hPutStrLn h "\n\\end{document}"
              hClose h
        Just (k, s) ->
            writeLnPreskel h m pp k s

-- XY-pic output

newtype Matrix = Matrix [Row]   -- Generates \xymatrix when shown

newtype Row = Row [Entry]       -- A row in the matrix

data Entry                      -- An entry in a row
    = Empty                     -- Leave entry blank.
    | Item { tag :: Maybe String, -- Maybe place text, else use bullet
             len :: Int,          -- Length of the strand succession arrow
                                  -- Communication arrows, bend arrows
             adj :: [(Int, Int, Bool, Bool)] } -- when second bool is True

-- Construct the matrix for a preskeleton
matrix :: Preskel -> Matrix
matrix k =
    Matrix (roles : [ row r | r <- nats (1 + maxRank k rank) ])
    where                       -- Column header is a role name
      roles = Row (map role (initial k))
      role v = Item { tag = Just ("\\txt{\\strut " ++ part (inst v) ++ "}"),
                      len = 0, adj = [] }
      row r = Row [ entry r c | c <- nats (strands k) ]
      entry r c =               -- Entry constructor
          case lookup (c, r) alist of
            Nothing -> Empty    -- Nothing at this grid point
            Just v -> item c r v -- Grid point has content
      item c r v =
          let len = maybe 0 (\v'->rank (vnode v') - r) (next v)
              adj = map (style c r v) (succs v) in
          Item { tag = Nothing, len = len, adj = map (curve c r) adj }
      style c r src dst =
          (msg src == msg dst, x, y)
          where
            (x, y) = shift (c, r) $ rankNode $ vnode dst
      curve c r (solid, x, y) = -- Decide if an arrows is curved
          (x, y, solid, abs x > 1 && bend)
          where
            bend = y /= 0 || any (obstruction c r) (between x)
      obstruction c r x =       -- Is there an obstruction here?
          maybe False (const True) (lookup (c + x, r) alist)
      between n =               -- List of ints toward zero
          case compare n 0 of
            LT -> [(n + 1)..(-1)]
            EQ -> []
            GT -> [1..(n - 1)]
      -- Map grid points with content to vertices
      alist = [ (rankNode (vnode v), v) | v <- vertices k ]
      rankNode (s, p) = (s, rank (s, p))
      rank = layout k     -- Get causally intuitive preskeleton layout

-- Shift origin of a pair
shift :: (Int, Int) -> (Int, Int) -> (Int, Int)
shift (x0, y0) (x1, y1) = (x1 - x0, y1 - y0)

-- A matrix is translated into LaTeX XY-pic macros using show methods

instance Show Matrix where
    showsPrec _ (Matrix []) =
        showString "\\relax"
    showsPrec _ (Matrix (r : rs)) =
        showString "$$\\xymatrix{\n" . shows r . rows rs . showString "}$$"
        where
          rows [] = id
          rows (r : rs) =
              showString "\\\\\n" . shows r . rows rs

instance Show Row where
    showsPrec _ (Row []) = id
    showsPrec _ (Row (e : es)) =
        shows e . entries es
        where
          entries [] = id
          entries (e : es) =
              showChar '&' . shows e . entries es

instance Show Entry where
    showsPrec _ Empty = id
    showsPrec _ (Item {tag = tag, len = len, adj = adj}) =
        element . succ . comm adj
        where
          element = showString (maybe "\\bullet" id tag)
          succ = if len > 0 then
                     showString "\\ar@{=>}[" . vert len . showChar ']'
                 else
                     id
          comm [] = id
          comm ((x, y, solid, bend) : cs) =
              showString ar . horiz x . vert y . showChar ']' . comm cs
              where
                curve = case () of
                        _ | not bend -> "["
                          | x > 0 -> "@/_/["
                          | otherwise -> "@/^/["
                style = if solid then "" else "@{-->}"
                ar = "\\ar" ++ style ++ curve

vert :: Int -> ShowS
vert n =
    case compare n 0 of
      LT -> showChar 'u' . vert (succ n)
      EQ -> id
      GT -> showChar 'd' . vert (pred n)

horiz :: Int -> ShowS
horiz n =
    case compare n 0 of
      LT -> showChar 'l' . horiz (succ n)
      EQ -> id
      GT -> showChar 'r' . horiz (pred n)

-- Writer
writeItem :: Handle -> Int -> Printer a -> SExpr a -> IO ()
writeItem h margin pp sexpr =
    hPutStrLn h (pp margin indent sexpr)

-- S-expression pretty print parameters
indent :: Int
indent = 2