packages feed

cpsa-2.2.10: src/CPSA/Graph/Main.hs

-- Generate views of a sequence of preskeletons

-- 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 Main (main) where

import Numeric
import System.IO
import System.Console.GetOpt
import CPSA.Lib.CPSA (PosHandle, SExpr, Pos)
import CPSA.Lib.Entry
import CPSA.Graph.Config
import CPSA.Graph.Loader
import CPSA.Graph.CompactView
import CPSA.Graph.ExpandedView
import CPSA.Graph.LaTeXView

-- Runtime parameters

data Params = Params
    { file :: Maybe FilePath,   -- Nothing specifies standard output
      format :: Format,         -- Output format
      prefix :: Bool,           -- Use prefix notation?
      scripted :: Bool,         -- Use scripting?
      margin :: Int }           -- Output line length
    deriving Show

data Format = XHTML | TreelessXHTML | SVG | LaTeX deriving Show

main :: IO ()
main =
    do
      (p, params) <- start options interp
      case format params of
        TreelessXHTML -> treeless p params
        LaTeX -> latex p params
        _ -> loadAll p params

-- Load all preskeletons before generating any output.
-- Don't use these graphing methods if your input is large.
loadAll :: PosHandle -> Params -> IO ()
loadAll p params =
    do
      preskels <- tryIO (loadDefs p)
      case preskels of
        Left err -> abort (show err)
        Right (cmts, preskels) ->
            do
              h <- outputHandle (file params)
              hPutStrLn h "<?xml version=\"1.0\"?>"
              hPutStrLn h ("<!-- " ++ cpsaVersion ++ " -->")
              let conf = config (prefix params) (scripted params)
              case format params of
                XHTML -> expandedView h (conf False)
                         (margin params) cmts preskels
                SVG -> compactView h (conf True) preskels
                _ -> error "Bad case in main"

-- Load comments and preskeletons
loadDefs :: PosHandle -> IO ([SExpr Pos], [Preskel])
loadDefs h =
    do
      (cmts, k, s) <- loadFirst h
      ks <- loop [k] s
      return (cmts, ks)
    where
      loop ks s =
          do
            n <- loadNext s
            case n of
              Nothing ->        -- EOF
                  return $ reverse ks
              Just (k, s) ->
                  loop (k:ks) s

-- XHTML graphing for very large files.
-- Treeless loads one S-expression at a time, processes it, prints the
-- results, and makes the S-expression available for garbage
-- collection before reading the next S-expression.
treeless :: PosHandle -> Params -> IO ()
treeless p params =
    do
      preskel <- tryIO (loadFirst p)
      case preskel of
        Left err -> abort (show err)
        Right (cmts, preskel, state) ->
            do
              h <- outputHandle (file params)
              hPutStrLn h "<?xml version=\"1.0\"?>"
              hPutStrLn h ("<!-- " ++ cpsaVersion ++ " -->")
              let conf = config (prefix params) (scripted params) False
              ans <- tryIO (treelessView h conf (margin params)
                                         cmts preskel state)
              case ans of
                Left err -> abort (show err)
                Right () -> return ()

-- LaTeX graphing.
latex :: PosHandle -> Params -> IO ()
latex p params =
    do
      preskel <- tryIO (loadFirst p)
      case preskel of
        Left err -> abort (show err)
        Right (cmts, preskel, state) ->
            do
              h <- outputHandle (file params)
              hPutStrLn h "\\documentclass[12pt]{article}"
              hPutStrLn h ("% " ++ cpsaVersion)
              let conf = config (prefix params) (scripted params) False
              let pp = printer conf
              ans <- tryIO (latexView h (margin params) pp cmts preskel state)
              case ans of
                Left err -> abort (show err)
                Right () -> return ()

-- Command line option flags
data Flag
    = Help                      -- Help
    | Info                      -- Version information
    | Expanded                  -- Select expanded format in XHTML
    | Treeless                  -- Select treeless expanded format in XHTML
    | Scripted                  -- Ensable scripting
    | Compact                   -- Select compact format in SVG
    | Text                      -- Select text format in LaTeX
    | Margin String             -- Output line length
    | InfixFlag                 -- Select output notation
    | Output String             -- Output file name
      deriving Show

defaultMargin :: Int
defaultMargin = optMargin defaultOptions

options :: [OptDescr Flag]
options =
    [ Option ['o'] ["output"]   (ReqArg Output "FILE") "output FILE",
      Option ['x'] ["expanded"] (NoArg Expanded)
      "use expanded format (default)",
      Option ['z'] ["zoom"]     (NoArg Scripted)
      "enable diagram scaling",
      Option ['t'] ["treeless"] (NoArg Treeless)
      "use treeless expanded format",
      Option ['c'] ["compact"]  (NoArg Compact)        "use compact format",
      Option ['l'] ["latex"]    (NoArg Text)           "use LaTeX format",
      Option ['m'] ["margin"]   (ReqArg Margin "INT")
      ("set output margin (default " ++ show defaultMargin ++ ")"),
      Option ['i'] ["infix"]    (NoArg InfixFlag) "output uses infix notation",
      Option ['h'] ["help"]     (NoArg Help)           "show help message",
      Option ['v'] ["version"]  (NoArg Info)           "show version number" ]

-- Interpret option flags
interp :: [Flag] -> IO Params
interp flags =
    loop flags (Params { file = Nothing, -- By default, no output file
                         format = XHTML, -- and use expanded format
                         prefix = True,
                         scripted = False,
                         margin = defaultMargin })
    where
      loop [] params = return params
      loop (Output name : flags) params
          | file params == Nothing =
              loop flags $ params { file = Just name }
      loop (Expanded : flags) params =
          loop flags $ params { format = XHTML }
      loop (Treeless : flags) params =
          loop flags $ params { format = TreelessXHTML }
      loop (Scripted : flags) params =
          loop flags $ params { scripted = True }
      loop (Compact : flags) params =
          loop flags $ params { format = SVG }
      loop (Text : flags) params =
          loop flags $ params { format = LaTeX }
      loop (InfixFlag : flags) params =
          loop flags $ params { prefix = False }
      loop (Margin value : flags) params =
          case readDec value of
            [(margin, "")] ->
                loop flags $ params { margin = margin }
            _ ->
                do
                  msg <- usage options ["Bad value for margin\n"]
                  abort msg
      loop (Info : _) _ =
          success cpsaVersion
      loop (Help : _) _ =
          do                    -- Show help then exit with success
            msg <- usage options []
            success msg
      loop _ _ =
           do                   -- Show help then exit with failure
             msg <- usage options ["Bad option combination\n"]
             abort msg

-- Default configuration.  The lengths are in points, however the more
-- natural choice is a font relative unit of length such as ems,
-- however FireFox doesn't support these units yet.
config :: Bool -> Bool -> Bool -> Config
config prefix scripts compact =
    Config { units = "pt",
             font = font,
             stroke = 0.08 * font,
             dash = 0.50 * font,
             gap = 0.20 * font,
             tx = 4.16 * font,
             ty = 6.25 * font,
             ta = 1.75 * font,
             td = 1.16 * font,
             dx = 8.33 * font,
             dy = 6.25 * font,
             mx = 3.33 * font,
             my = 3.33 * font,
             br = 0.50 * font,
             compact = compact,
             notation = if prefix then Prefix else Infix,
             scripts = scripts }
    where
      font = 12