packages feed

cpsa-4.4.3: src/CPSA/Debranch/Main.hs

-- Expand Zappa branching

-- 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 System.IO
import CPSA.Lib.SExpr
import CPSA.Lib.Printer (pp)
import CPSA.Lib.Entry
import CPSA.Lib.Expand (readSExprs)

main :: IO ()
main =
    do
      (p, (output, margin)) <- start filterOptions filterInterp
      sexprs <- readSExprs p
      h <- outputHandle output
      _ <- mapM (writeCpsaLn (pp margin defaultIndent) h) (map debranch sexprs)
      hClose h

writeCpsaLn :: (SExpr a -> String) -> Handle -> SExpr a -> IO ()
writeCpsaLn printer h sexpr =
    do
      hPutStrLn h $ printer sexpr
      hPutStrLn h ""

-- Type of an environment used for substitutions
type Env = [(String, SExpr Pos)]

-- Debranch a protocol and leave everything else alone.
debranch :: SExpr Pos -> SExpr Pos
debranch (L pos (S _ "defprotocol" : S _ name : S _ alg : xs)) =
    L pos (S pos "defprotocol" : S pos name : S pos alg :
             concatMap protBody xs)
debranch (L pos (S _ "defprotocol" : _)) =
    error (shows pos "Bad protocol")
debranch x = x

-- Debranch the body of a protocol focusing in on roles.
protBody :: SExpr Pos -> [SExpr Pos]
protBody (L pos (S _ "defrole" : S _ name : vars :
                   L _ (S _ "trace" : events) : alist)) =
    let traceEnvs = branchSplits events in
    if length traceEnvs == 1 then -- No branching in this role
        [defrole pos name vars (fst (head traceEnvs)) alist]
    else
        map f (zip [0..] traceEnvs)
        where
          f :: (Int, ([SExpr Pos], Env)) -> SExpr Pos
          f (i, (trace, env)) =
              defrole pos (name ++ show i) vars
                          (map (subst env) trace)
                          (map (g env) alist)
          g :: Env -> SExpr Pos -> SExpr Pos
          g env (L pos (s@(S _ _) : args)) =
              L pos (s : map (subst env) args)
          g _ x = x
protBody (L pos (S _ "defrole" : _)) =
    error (shows pos "Bad role")
protBody x = [x]

-- Find splits in a trace and return environments made from cheq's.
branchSplits :: [SExpr Pos] -> [([SExpr Pos], Env)]
branchSplits [] = [([], [])]
branchSplits [L _ (S _ "branch" : branches)] =
    ([], []) : concatMap f branches
    where
      f (L _ branch) = branchSplits branch
      f _ = [([], [])]
branchSplits (L _ [S _ "cheq", S _ var, term] : events) =
    map f (branchSplits events)
    where
      f (trace, env) = (trace, g ((var, term) : env))
      substEnv env (v, t) = (v, subst env t)
      g env = map (substEnv env) env
branchSplits (event : events) =
    map f (branchSplits events)
    where
      f (trace, env) = (event : trace, env)

-- Construct a role from its parts.
defrole :: Pos -> String -> SExpr Pos -> [SExpr Pos] -> [SExpr Pos] -> SExpr Pos
defrole pos name vars trace alist =
    L pos (S pos "defrole" : S pos name : vars
                 : L pos (S pos "trace" : trace) : alist)

-- Substitute variables in a term when they are in an environment.
subst :: Env -> SExpr Pos -> SExpr Pos
subst env s@(S _ sym) =
    case lookup sym env of
      Just term -> term
      Nothing -> s
subst env (L pos (S _ fun : terms)) =
    L pos (S pos fun : map (subst env) terms)
subst _ x = x