cpsa-3.6.6: src/CPSA/Latex/Main.hs
-- Convert macros into LaTeX macros
-- Copyright (c) 2020 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 Data.List
import System.Environment
import System.IO
import System.Console.GetOpt
import Control.Monad
import CPSA.Lib.SExpr
import CPSA.Lib.Expand
import CPSA.Lib.Entry hiding (defaultOptions, Options, optFile)
main :: IO ()
main =
do
argv <- getArgs
(flags, files) <- opts options argv
opts <- interp defaultOptions flags
p <- openInput files
sexprs <- readSExprs p
macros <- macroExtract sexprs
let filtered = macroFilter macros
outputResults opts (reverse $ map translateMacro filtered)
-- Command line option flags
data Flag
= Output String
| Help
| Info
deriving Show
data Options = Options {
optFile :: Maybe FilePath
}
deriving Show
defaultOptions :: Options
defaultOptions = Options {
optFile = Nothing
}
options :: [OptDescr Flag]
options =
[ Option ['o'] ["output"] (ReqArg Output "FILE") "output FILE",
Option ['h'] ["help"] (NoArg Help) "show help message",
Option ['v'] ["version"] (NoArg Info) "show version number" ]
opts :: [OptDescr a] -> [String] -> IO ([a], [String])
opts options argv =
case getOpt RequireOrder options argv of
(o, n, []) -> return (o, n)
(_, _, errs) ->
do
msg <- usage options errs
abort msg
-- Interpret option flags
interp :: Options -> [Flag] -> IO Options
interp opts flags =
loop flags opts
where
loop [] opts = return opts
loop (Output name : flags) opts
| optFile opts == Nothing =
loop flags $ opts { optFile = Just name }
loop (Help : _) _ =
do -- Show help then exit with success
msg <- usage options []
success msg
loop (Info : _) _ =
success cpsaVersion
loop _ _ =
do -- Show help then exit with failure
msg <- usage options ["Bad option combination\n"]
abort msg
openInput :: [String] -> IO PosHandle
openInput [file] =
do -- Input from named file
input <- openFile file ReadMode
posHandle file input
openInput [] =
posHandle "" stdin -- Input from the standard input
openInput _ =
do
msg <- usage options ["too many input files\n"]
abort msg
-- Write results to specified output location
outputResults :: Options -> [String] -> IO ()
outputResults opts rs =
do
h <- outputHandle (optFile opts)
mapM_ (hPutStrLn h) rs
hClose h
return ()
-- The mechanisms for translating from CPSA to LaTeX are below
-- Extract the macros from a list of SExprs
macroExtract :: [SExpr Pos] -> IO [Macro]
macroExtract sexprs = do
(mList, _) <- foldM (expandSExpr bound) ([], []) sexprs
return mList
-- Get rid of splices
macroRemoveSplices :: [Macro] -> [Macro]
macroRemoveSplices [] = []
macroRemoveSplices (m:ms) =
case (getMacroBody m) of
L _ (S _ "^":_) -> macroRemoveSplices ms
_ -> m:(macroRemoveSplices ms)
-- Get rid of defprotocols
macroRemoveDefprotocols :: [Macro] -> [Macro]
macroRemoveDefprotocols [] = []
macroRemoveDefprotocols (m:ms) =
case (getMacroBody m) of
L _ (S _ "defprotocol":_) -> macroRemoveDefprotocols ms
_ -> m:(macroRemoveDefprotocols ms)
-- Get rid of defroles
macroRemoveDefroles :: [Macro] -> [Macro]
macroRemoveDefroles [] = []
macroRemoveDefroles (m:ms) =
case (getMacroBody m) of
L _ (S _ "defrole":_) -> macroRemoveDefroles ms
_ -> m:(macroRemoveDefroles ms)
-- Get rid of rules
macroRemoveRules :: [Macro] -> [Macro]
macroRemoveRules [] = []
macroRemoveRules (m:ms) =
case (getMacroBody m) of
L _ (S _ "defrule":_) -> macroRemoveRules ms
_ -> m:(macroRemoveRules ms)
-- Get rid of goals
macroRemoveGoals :: [Macro] -> [Macro]
macroRemoveGoals [] = []
macroRemoveGoals (m:ms) =
case (getMacroBody m) of
L _ (S _ "defgoal":_) -> macroRemoveGoals ms
_ -> m:(macroRemoveGoals ms)
-- Composition of the preceding three functions
macroFilter :: [Macro] -> [Macro]
macroFilter = macroRemoveDefroles .
macroRemoveDefprotocols .
macroRemoveGoals .
macroRemoveRules .
macroRemoveSplices
-- Given a macro, return the count of its args
macroArgsCount :: Macro -> Int
macroArgsCount = length . getMacroArgs
-- Translate an SExpr into a String and a final translation
-- environment given some initial environment
translateSExpr :: [(String, String)] -> SExpr a -> (String, [(String, String)])
translateSExpr e (S _ s) =
case (lookup s e) of
Nothing -> let t = (translateSymbol s) in (t, (s,t):e)
Just t -> (t, e)
translateSExpr e (Q _ s) = let t = translateQuotedStr s in (t, (s,t):e)
translateSExpr e (N _ n) = (translateInt n, e)
translateSExpr e (L _ []) = ("", e)
translateSExpr e (L _ (S _ "cat":ys)) =
(intercalate "\\cat " xs, e1)
where
(xs, e1) = translateSExprFold e ys
translateSExpr e (L p (S q "enc":ys)) =
("\\enc{" ++ s1 ++ "}{" ++ s2 ++ "}", e2)
where
(s2, e1) = translateSExpr e (last ys)
(s1, e2) = translateSExpr e1 (L p (S q "cat":init ys))
translateSExpr e (L p (S q "hash":ys)) =
("\\hash{" ++ s ++ "}", e')
where
(s, e') = translateSExpr e (L p (S q "cat":ys))
translateSExpr e (L _ [S _ "pubk", y1, y2]) =
case translateSExprFold e [y1, y2] of
([s1, s2], e1) ->
("\\pubktwo{" ++ s1 ++ "}{" ++ s2 ++ "}", e1)
_ ->
error "translateSExpr: bad fold for pubktwo"
translateSExpr e (L _ [S _ "privk", y1, y2]) =
case translateSExprFold e [y1, y2] of
([s1, s2], e1) ->
("\\privktwo{" ++ s1 ++ "}{" ++ s2 ++ "}", e1)
_ ->
error "translateSExpr: bad fold for privktwo"
translateSExpr e (L _ ys) =
(concat xs, e1)
where
(xs, e1) = translateSExprFold e ys
translateSExprFold :: [(String, String)] -> [SExpr a] ->
([String], [(String, String)])
translateSExprFold e ys = (xs, e')
where
(xs, e') = foldr f ([], e) ys
f y (acc, env) = (x:acc, e'')
where (x, e'') = translateSExpr env y
-- Translate a CPSA macro into an equivalent LaTeX macro
translateMacro :: Macro -> String
translateMacro m =
let name = toMathMode $ getMacroName m
args = getMacroArgs m
argc = macroArgsCount m
body = getMacroBody m
env = zip args ["{#" ++ (show i) ++ "}" | i <- [(1::Int)..]]
prelude = "\\newcommand{\\"
l = "}["
r = "]{"
in prelude++name++l++(show argc)++r++(fst $ translateSExpr env body)++"}"
-- Symbol to String translation rules
translateSymbol :: String -> String
translateSymbol s = "\\" ++ (toMathMode s)
-- Quoted string to String translation rules
translateQuotedStr :: String -> String
translateQuotedStr s = "\\tag{" ++ s' ++ "}"
where s' = toMathModeSpaced s
-- Delete chars LaTeX math mode doesn't like
toMathMode :: String -> String
toMathMode s = [c | c<-s, not (c `elem` badChars)]
where badChars = " -"
-- Int to String translation rules
translateInt :: Int -> String
translateInt n = show n
-- Replace chars LaTeX math mode doesn't like with "\\ "
toMathModeSpaced :: String -> String
toMathModeSpaced s = intercalate "\\ " (f s)
where badChars = " -"
f :: String -> [String]
f s = case dropWhile (`elem` badChars) s of
"" -> []
s' -> w : f s''
where (w, s'') = break (`elem` badChars) s'