tamarin-prover-0.4.0.0: src/Theory/Parser.hs
{-# LANGUAGE TupleSections #-}
-- |
-- Copyright : (c) 2010-2012 Simon Meier, Benedikt Schmidt
-- License : GPL v3 (see LICENSE)
--
-- Maintainer : Simon Meier <iridcode@gmail.com>
-- Portability : portable
--
-- Parsing protocol theories.
module Theory.Parser (
parseOpenTheory
, parseOpenTheoryString
, parseProofMethod
, parseLemma
, parseIntruderRulesDH
) where
import Prelude hiding (id, (.))
import Data.Char (toUpper, isUpper, isDigit)
import Data.Foldable (asum)
import Data.Label
import qualified Data.Set as S
import qualified Data.Map as M
import Data.Monoid
import qualified Data.ByteString.Char8 as BC
import Control.Monad
import Control.Applicative hiding (empty, many, optional)
import Control.Category
import Text.Parsec.Pos
import Text.Parsec hiding (token, (<|>), string )
import qualified Text.Parsec as P
import Theory.Lexer
( Keyword(..), TextType(..), runAlex, AlexPosn(..)
, alexGetPos, alexMonadScan
)
import Term.SubtermRule
import Term.Substitution
import Text.PrettyPrint.Class (render)
import Theory
------------------------------------------------------------------------------
-- Specializing Parsec to our needs
------------------------------------------------------------------------------
-- Scanner
----------
-- | The tokens delivered by our Alex based scanner
type Token = (SourcePos, Keyword)
-- | Scan a string using the given filename in the error messages.
--
-- NOTE: Lexical errors are thrown using 'error'.
scanString :: FilePath -> String -> [Token]
scanString filename s =
case runAlex s gatherUntilEOF of
Left err -> error err
Right kws -> kws
where
gatherUntilEOF = do
AlexPn _ line col <- alexGetPos
let pos = newPos filename line col
k <- alexMonadScan
case k of
EOF -> return [(pos,EOF)]
_ -> do kws <- gatherUntilEOF
return $ (pos,k) : kws
-- Parser
---------
-- | A parser for a stream of tokens.
type Parser a = Parsec [Token] MaudeSig a
-- | Parse a token based on the acceptance condition
token :: (Keyword -> Maybe a) -> Parser a
token p = P.token (show . snd) fst (p . snd)
-- | Parse a term.
kw :: Keyword -> Parser ()
kw t = token check
where
check t' | t == t' = Just () | otherwise = Nothing
-- | Parse content between keywords.
betweenKWs :: Keyword -> Keyword -> Parser a -> Parser a
betweenKWs l r = between (kw l) (kw r)
{-
-- | Between braces.
braced :: Parser a -> Parser a
braced = betweenKWs LBRACE RBRACE
-}
-- | Between parentheses.
parens :: Parser a -> Parser a
parens = betweenKWs LPAREN RPAREN
-- | Between single quotes.
singleQuoted :: Parser a -> Parser a
singleQuoted = betweenKWs SQUOTE SQUOTE
-- | Between double quotes.
doubleQuoted :: Parser a -> Parser a
doubleQuoted = betweenKWs DQUOTE DQUOTE
-- | Parse an identifier as a string
identifier :: Parser String
identifier = token extract
where extract (IDENT name)
-- don't allow certain reserved words as identifiers
| not (name `elem` ["in","let","rule"]) = Just name
extract _ = Nothing
-- | Parse an identifier as a string
string :: String -> Parser ()
string cs = token extract
where extract (IDENT name) | cs == name = Just ()
extract _ = Nothing
-- | Parse a sequence of fixed strings.
strings :: [String] -> Parser ()
strings = mapM_ string
-- | Parse an integer.
integer :: Parser Int
integer = do i <- identifier
guard (all isDigit i)
return (read i)
-- | A comma separated list of elements.
commaSep :: Parser a -> Parser [a]
commaSep = (`sepBy` kw COMMA)
{-
-- | A comma separated non-empty list of elements.
commaSep1 :: Parser a -> Parser [a]
commaSep1 = (`sepBy1` kw COMMA)
-}
-- | Parse a list of items '[' item ',' ... ',' item ']'
list :: Parser a -> Parser [a]
list p = kw LBRACKET *> commaSep p <* kw RBRACKET
-- | A formal comment; i.e., (header, body)
formalComment :: Parser (String, String)
formalComment =
(,) <$> text begin
<*> (concat <$> many (text content) <* text end)
where
text f = token (\t -> case t of TEXT ty -> f ty; _ -> mzero)
begin (TextBegin str) = return str
begin _ = mzero
content (TextContent str) = return str
content _ = mzero
end (TextEnd) = return ()
end _ = mzero
------------------------------------------------------------------------------
-- Lexing and parsing theory files and proof methods
------------------------------------------------------------------------------
-- | Parser a file.
parseFile :: Parser a -> FilePath -> IO a
parseFile parser f = do
s <- readFile f
case runParser parser minimalMaudeSig f (scanString f s) of
Right p -> return p
Left err -> error $ show err
-- | Parse a security protocol theory file.
parseOpenTheory :: [String] -- ^ Defined flags
-> FilePath -> IO OpenTheory
parseOpenTheory flags = parseFile (theory flags)
-- | Parse DH intruder rules.
parseIntruderRulesDH :: FilePath -> IO [IntrRuleAC]
parseIntruderRulesDH = parseFile (setState dhMaudeSig >> many intrRule)
-- | Parse a security protocol theory file.
-- TODO: This function seems to parse a string, not a file from a file path?
parseProofMethod :: FilePath -> Either ParseError ProofMethod
parseProofMethod =
runParser proofMethod minimalMaudeSig dummySource . scanString dummySource
where
dummySource = "<interactive>"
-- | Parse a security protocol theory from a string.
parseOpenTheoryString :: [String] -- ^ Defined flags.
-> String -> Either ParseError OpenTheory
parseOpenTheoryString flags = parseFromString (theory flags)
-- | Parse a lemma for an open theory from a string.
parseLemma :: String -> Either ParseError (Lemma ProofSkeleton)
parseLemma = parseFromString lemma
-- | Run a given parser on a given string.
parseFromString :: Parser a -> String -> Either ParseError a
parseFromString parser =
runParser parser minimalMaudeSig dummySource . scanString dummySource
where
dummySource = "<interactive>"
------------------------------------------------------------------------------
-- Parsing Terms
------------------------------------------------------------------------------
{-
BNF: Not completely up to date...
theory := 'theory' ident 'begin' protocol 'end'
protocol := rules
rules := rule | rule rules
intrrule := ident '[' intrinfo ']' ':' terms '-->' terms
intrinfo := 'Destr' | 'Constr'
protorule := ident ':' factList '-->' factList
factList := '[' [facts] ']'
facts := fact | fact ',' facts
protoFact := ident '(' terms ')'
terms := term | term ',' terms
term := lit | application | '<' term ',' terms '>' -- right assoc pairing
application := ident '(' terms ')'
lit := ident | '\'' ident '\''
ident := <a-zA-Z> (<a-zA-Z0-9-_)
// example protocol rule
Init_1:
[ Pub(I), Pub(R), Fresh(ni) ]
-->
[ Init_1(I,R,ni), Send(encA(pk(R), <I,R,ni>)) ]
// example intruder rule
Exp [Constr]:
[ (x^_((x1*x2))), ((x3*x1)*_x4) ]
-->
[ (x^(x3*_((x4*x2)))) ]
-}
------------------------------------------------------------------------------
-- Parsing Terms
------------------------------------------------------------------------------
-- | Parse an identifier possibly indexed with a number.
indexedIdentifier :: Parser (String, Integer)
indexedIdentifier = do
(,) <$> identifier
<*> option 0 (try (kw DOT *> (fromIntegral <$> integer)))
-- | Parse a logical variable with the given sorts allowed.
sortedLVar :: [LSort] -> Parser LVar
sortedLVar ss =
asum $ map (try . mkSuffixParser) ss ++ map mkPrefixParser ss
where
mkSuffixParser s = do
(n, i) <- indexedIdentifier
kw COLON
string (sortSuffix s)
return (LVar n s i)
mkPrefixParser s = do
case s of
LSortMsg -> pure ()
LSortPub -> kw DOLLAR
LSortFresh -> kw TILDE
LSortNode -> kw SHARP
LSortMSet -> kw PERCENT
(n, i) <- indexedIdentifier
return (LVar n s i)
-- | An arbitrary logical variable.
lvar :: Parser LVar
lvar = sortedLVar [minBound..]
-- | Parse a non-node variable.
msgvar :: Parser LVar
msgvar = sortedLVar [LSortFresh, LSortPub, LSortMsg, LSortMSet]
-- | Parse a graph node variable.
nodevar :: Parser NodeId
nodevar = asum
[ sortedLVar [LSortNode]
, (\(n, i) -> LVar n LSortNode i) <$> indexedIdentifier ]
<?> "node"
-- | Parse an lit with logical variables.
llit :: Parser LNTerm
llit = asum
[ freshTerm <$> try (kw TILDE *> singleQuoted identifier) <?> "fresh name"
, pubTerm <$> singleQuoted identifier <?> "public name"
, varTerm <$> msgvar
]
-- | Lookup the arity of a non-ac symbol. Fails with a sensible error message
-- if the operator is not known.
lookupNonACArity :: String -> Parser Int
lookupNonACArity op = do
maudeSig <- getState
case lookup (BC.pack op) (S.toList $ allFunctionSymbols maudeSig) of
Nothing -> fail $ "unknown operator `" ++ op ++ "'"
Just k -> return k
-- | Parse an n-ary operator application for arbitrary n.
naryOpApp :: Ord l => Parser (Term l) -> Parser (Term l)
naryOpApp plit = do
op <- identifier
k <- lookupNonACArity op
ts <- parens $ if k == 1
then return <$> tupleterm plit
else sepBy (multterm plit) (kw COMMA)
let k' = length ts
when (k /= k') $
fail $ "operator `" ++ op ++"' has arity " ++ show k ++
", but here it is used with arity " ++ show k'
return $ fAppNonAC (BC.pack op, k') ts
-- | Parse a binary operator written as @op{arg1}arg2@.
binaryAlgApp :: Ord l => Parser (Term l) -> Parser (Term l)
binaryAlgApp plit = do
op <- identifier
k <- lookupNonACArity op
arg1 <- kw LBRACE *> tupleterm plit <* kw RBRACE
arg2 <- term plit
when (k /= 2) $ fail $
"only operators of arity 2 can be written using the `op{t1}t2' notation"
return $ fAppNonAC (BC.pack op, 2) [arg1, arg2]
-- | Parse a term.
term :: Ord l => Parser (Term l) -> Parser (Term l)
term plit = asum
[ pairing <?> "pairs"
, parens (multterm plit)
, string "1" *> pure fAppOne
, application <?> "function application"
, nullaryApp
, plit
]
<?> "term"
where
application = asum $ map (try . ($ plit)) [naryOpApp, binaryAlgApp]
pairing = kw LESS *> tupleterm plit <* kw GREATER
nullaryApp = do
maudeSig <- getState
asum [ try (string (BC.unpack sym)) *> pure (fApp (NonAC (sym,0)) [])
| (sym,0) <- S.toList $ allFunctionSymbols maudeSig ]
-- | A left-associative sequence of exponentations.
expterm :: Ord l => Parser (Term l) -> Parser (Term l)
expterm plit = chainl1 (term plit) ((\a b -> fAppExp (a,b)) <$ kw HAT)
-- | A left-associative sequence of multiplications.
multterm :: Ord l => Parser (Term l) -> Parser (Term l)
multterm plit = do
dh <- enableDH <$> getState
if dh -- if DH is not enabled, do not accept 'multterm's and 'expterm's
then chainl1 (expterm plit) ((\a b -> fAppMult [a,b]) <$ kw STAR)
else term plit
-- | A right-associative sequence of tuples.
tupleterm :: Ord l => Parser (Term l) -> Parser (Term l)
tupleterm plit = chainr1 (multterm plit) ((\a b -> fAppPair (a,b))<$ kw COMMA)
-- | Parse a fact.
fact :: Ord l => Parser (Term l) -> Parser (Fact (Term l))
fact plit =
do multi <- option Linear (kw BANG *> pure Persistent)
i <- identifier
case i of
[] -> fail "empty identifier"
(c:_) | isUpper c -> return ()
| otherwise -> fail "facts must start with upper-case letters"
ts <- parens (sepBy (multterm plit) (kw COMMA))
mkProtoFact multi i ts
<?> "protocol fact"
where
singleTerm _ constr [t] = return $ constr t
singleTerm f _ ts = fail $ "fact '" ++ f ++ "' used with arity " ++
show (length ts) ++ " instead of arity one"
mkProtoFact multi f = case map toUpper f of
"OUT" -> singleTerm f outFact
"IN" -> singleTerm f inFact
"KU" -> return . Fact KUFact
"KD" -> return . Fact KDFact
"DED" -> return . Fact DedFact
"FR" -> singleTerm f freshFact
_ -> return . protoFact multi f
------------------------------------------------------------------------------
-- Parsing Rules
------------------------------------------------------------------------------
-- | Parse a "(modulo ..)" information.
modulo :: String -> Parser ()
modulo thy = parens $ strings ["modulo", thy]
moduloE, moduloAC :: Parser ()
moduloE = modulo "E"
moduloAC = modulo "AC"
{-
-- | Parse a typing assertion modulo E.
typeAssertions :: Parser TypingE
typeAssertions = fmap TypingE $
do try (strings ["type", "assertions"])
optional moduloE
kw COLON
many1 ((,) <$> (try (msgvar <* kw COLON))
<*> ( commaSep1 (try $ multterm llit) <|>
(kw MINUS *> pure [])
)
)
<|> pure []
-}
-- | Parse a protocol rule. For the special rules 'Reveal_fresh', 'Fresh',
-- 'Knows', and 'Learn' no rule is returned as the default theory already
-- contains them.
protoRule :: Parser (ProtoRuleE)
protoRule = do
name <- try (string "rule" *> optional moduloE *> identifier <* kw COLON)
subst <- option emptySubst letBlock
(ps,as,cs) <- genericRule
return $ apply subst $ Rule (StandRule name) ps cs as
-- | Parse a let block with bottom-up application semantics.
letBlock :: Parser LNSubst
letBlock = do
toSubst <$> (string "let" *> many1 definition <* string "in")
where
toSubst = foldr1 compose . map (substFromList . return)
definition = (,) <$> (sortedLVar [LSortMsg] <* kw EQUAL) <*> multterm llit
-- | Parse an intruder rule.
intrRule :: Parser IntrRuleAC
intrRule = do
info <- try (string "rule" *> moduloAC *> intrInfo <* kw COLON)
(ps,as,cs) <- genericRule
return $ Rule info ps cs as
where
intrInfo = do
name <- identifier
case name of
'c':cname -> return $ ConstrRule cname
'd':dname -> return $ DestrRule dname
_ -> fail $ "invalid intruder rule name '" ++ name ++ "'"
genericRule :: Parser ([LNFact], [LNFact], [LNFact])
genericRule =
(,,) <$> list (fact llit)
<*> ((pure [] <* kw LONGRIGHTARROW) <|>
(kw MINUS *> kw MINUS *> list (fact llit) <* kw RIGHTARROW))
<*> list (fact llit)
{-
-- | Add facts to a rule.
addFacts :: String -- ^ Command to be used: add_concs, add_prems
-> Parser (String, [LNFact])
addFacts cmd =
(,) <$> (string cmd *> identifier <* kw COLON) <*> commaSep1 fact
-}
------------------------------------------------------------------------------
-- Parsing transfer notation
------------------------------------------------------------------------------
{-
-- | Parse an lit with strings for both constants as well as variables.
tlit :: Parser TTerm
tlit = asum
[ constTerm <$> singleQuoted identifier
, varTerm <$> identifier
]
-- | Parse a single transfer.
transfer :: Parser Transfer
transfer = do
tf <- (\l -> Transfer l Nothing Nothing) <$> identifier <* kw DOT
(do right <- kw RIGHTARROW *> identifier <* kw COLON
desc <- transferDesc
return $ tf { tfRecv = Just (desc right) }
<|>
do right <- kw LEFTARROW *> identifier <* kw COLON
descr <- transferDesc
(do left <- try $ identifier <* kw LEFTARROW <* kw COLON
descl <- transferDesc
return $ tf { tfSend = Just (descr right)
, tfRecv = Just (descl left) }
<|>
do return $ tf { tfSend = Just (descr right) }
)
<|>
do left <- identifier
(do kw RIGHTARROW
(do right <- identifier <* kw COLON
desc <- transferDesc
return $ tf { tfSend = Just (desc left)
, tfRecv = Just (desc right) }
<|>
do descl <- kw COLON *> transferDesc
(do right <- kw RIGHTARROW *> identifier <* kw COLON
descr <- transferDesc
return $ tf { tfSend = Just (descl left)
, tfRecv = Just (descr right) }
<|>
do return $ tf { tfSend = Just (descl left) }
)
)
<|>
do kw LEFTARROW
(do desc <- kw COLON *> transferDesc
return $ tf { tfRecv = Just (desc left) }
<|>
do right <- identifier <* kw COLON
desc <- transferDesc
return $ tf { tfSend = Just (desc right)
, tfRecv = Just (desc left) }
)
)
)
where
transferDesc = do
ts <- tupleterm tlit
moreConcs <- (string "note" *> many1 (try $ fact tlit))
<|> pure []
types <- typeAssertions
return $ \a -> TransferDesc a ts moreConcs types
-- | Parse a protocol in transfer notation
transferProto :: Parser [ProtoRuleE]
transferProto = do
name <- string "anb" *> kw MINUS *> string "proto" *> identifier
braced (convTransferProto name <$> abbrevs <*> many1 transfer)
where
abbrevs = (string "let" *> many1 abbrev) <|> pure []
abbrev = (,) <$> try (identifier <* kw EQUAL) <*> multterm tlit
-}
------------------------------------------------------------------------------
-- Parsing Proofs
------------------------------------------------------------------------------
{-
-- | Parse a node premise.
nodePrem :: Parser NodePrem
nodePrem = NodePrem <$> parens ((,) <$> nodevar <*> (kw COMMA *> integer))
-- | Parse a node conclusion.
nodeConc :: Parser NodeConc
nodeConc = NodeConc <$> parens ((,) <$> nodevar <*> (kw COMMA *> integer))
-}
-- | Parse the @\@@ requires operator.
actionOp :: Parser ()
actionOp = try (kw AT)
-- | Parse the @<@ temporal less operator.
edgeOp :: Parser ()
edgeOp = try (kw GREATER *> kw RIGHTARROW)
-- | Parse the @<@ temporal less operator.
lessOp :: Parser ()
lessOp = try (kw LESS)
-- | Parse the @=@ equal operator.
equalOp :: Parser ()
equalOp = kw APPROX <|> kw EQUAL
-- | Parse the @--|@ deduced before operator.
dedBeforeOp :: Parser ()
dedBeforeOp = try (kw MINUS *> kw MINUS *> kw MID)
{-
-- | Parse the @~~>@ chain operator.
chainOp :: Parser ()
chainOp = kw TILDE *> kw TILDE *> kw TILDE *> kw GREATER
-}
-- | Parse a goal.
goal :: Parser Goal
goal = fail "SM: reimplement goal parsing" {- asum
[ splitGoal
, premiseGoal
, chainGoal
]
where
premiseGoal = try $ do
v <- nodevar
i <- brackets integer <* requiresOp
fa <- fact llit
return $ PremiseGoal fa (NodePrem (v, i))
chainGoal = ChainGoal
<$> (try $ term llit <* kw COLON)
<*> (Chain <$> (nodeConc <* chainOp) <*> nodePrem)
splitGoal = do
split <- (string "splitEqsOn" *> pure SplitEqs) <|>
(string "splitTypingOn" *> pure SplitTyping)
SplitGoal split <$> parens integer
-}
-- | Parse a proof method.
proofMethod :: Parser ProofMethod
proofMethod = optional (kw BANG) *> asum
[ string "sorry" *> pure (Sorry "not yet proven")
, string "simplify" *> pure Simplify
, string "solve" *> (SolveGoal <$> parens goal)
, string "contradiction" *> pure (Contradiction Nothing)
]
-- | Parse a proof skeleton.
proofSkeleton :: Parser ProofSkeleton
proofSkeleton =
finalProof <|> interProof
where
finalProof = do
method <- string "by" *> proofMethod
return (LNode (ProofStep method ()) M.empty)
interProof = do
method <- proofMethod
cases <- (sepBy oneCase (string "next") <* string "qed") <|>
((return . ("",)) <$> proofSkeleton )
return (LNode (ProofStep method ()) (M.fromList cases))
oneCase = (,) <$> (string "case" *> identifier) <*> proofSkeleton
------------------------------------------------------------------------------
-- Parsing Formulas and Lemmas
------------------------------------------------------------------------------
-- | Parse an atom with possibly bound logical variables.
blatom :: Parser BLAtom
blatom = (fmap (fmapTerm (fmap Free))) <$> asum
[ flip Action <$> try (fact llit <* actionOp) <*> nodevarTerm <?> "action"
, Less <$> try (nodevarTerm <* lessOp) <*> nodevarTerm <?> "less"
, DedBefore <$> try (term llit <* dedBeforeOp) <*> nodevarTerm <?> "deduced before"
, EdgeA <$> try (nodeConc <* edgeOp) <*> nodePrem <?> "edge"
, EqE <$> try (multterm llit <* equalOp) <*> multterm llit <?> "term equality"
, EqE <$> (nodevarTerm <* equalOp) <*> nodevarTerm <?> "node equality"
]
where
nodevarTerm = (lit . Var) <$> nodevar
nodePrem = annNode PremIdx
nodeConc = annNode ConcIdx
annNode mkAnn = parens ((,) <$> (nodevarTerm <* kw COMMA)
<*> (mkAnn <$> integer))
-- | Parse an atom of a formula.
fatom :: Parser (LFormula Name)
fatom = asum
[ pure lfalse <* string "F"
, pure ltrue <* string "T"
, Ato <$> try blatom
, quantification
, parens iff
]
where
quantification = do
q <- (pure forall <* (kw FORALL <|> string "All")) <|>
(pure exists <* (kw EXISTS <|> string "Ex") )
vs <- many1 lvar <* kw DOT
f <- iff
return $ foldr (hinted q) f vs
hinted :: ((String, LSort) -> LVar -> a) -> LVar -> a
hinted f v@(LVar n s _) = f (n,s) v
-- | Parse a negation.
negation :: Parser (LFormula Name)
negation = ((kw LNOT <|> string "not") *> (Not <$> fatom)) <|> fatom
-- | Parse a left-associative sequence of conjunctions.
conjuncts :: Parser (LFormula Name)
conjuncts = chainl1 negation ((.&&.) <$ (kw LAND <|> kw AND))
-- | Parse a left-associative sequence of disjunctions.
disjuncts :: Parser (LFormula Name)
disjuncts = chainl1 conjuncts ((.||.) <$ (kw LOR <|> kw MID))
-- | An implication.
imp :: Parser (LFormula Name)
imp = do
lhs <- disjuncts
asum [ try (kw EQUAL *> kw EQUAL *> kw GREATER) *>
((lhs .==>.) <$> imp)
, pure lhs ]
-- | An logical equivalence.
iff :: Parser (LFormula Name)
iff = do
lhs <- imp
asum [ try (kw LESS *> kw EQUAL *> kw GREATER) *>
((lhs .<=>.) <$> imp)
, pure lhs ]
-- | Parse a 'LemmaAttribute'.
lemmaAttribute :: Parser LemmaAttribute
lemmaAttribute = asum
[ string "typing" *> pure TypingLemma
, string "reuse" *> pure ReuseLemma
, string "invariant" *> pure InvariantLemma
]
-- | Parse a 'TraceQuantifier'.
traceQuantifier :: Parser TraceQuantifier
traceQuantifier = asum
[ string "all" *> kw MINUS *> string "traces" *> pure AllTraces
, string "exists" *> kw MINUS *> string "trace" *> pure ExistsTrace
]
-- | Parse a lemma.
lemma :: Parser (Lemma ProofSkeleton)
lemma = skeletonLemma <$> (string "lemma" *> optional moduloE *> identifier)
<*> (option [] $ list lemmaAttribute)
<*> (kw COLON *> option AllTraces traceQuantifier)
<*> doubleQuoted iff
<*> (proofSkeleton <|> pure (unproven ()))
-- | Parse a globally fresh 'FactTag' written as
--
-- fresh proto/2
globallyFresh :: Parser (S.Set FactTag)
globallyFresh =
string "unique_insts" *> kw COLON *>
(S.fromList <$> sepBy1 factSymbol (kw COMMA))
where
factSymbol =
ProtoFact Linear <$> identifier <*> (kw SLASH *> integer)
builtins :: Parser ()
builtins =
string "builtins" *> kw COLON *> sepBy1 builtinTheory (kw COMMA) *> pure ()
where
extendSig msig = modifyState (`mappend` msig)
builtinTheory = asum
[ try (string "diffie" *> kw MINUS *> string "hellman")
*> extendSig dhMaudeSig
, try (string "symmetric" *> kw MINUS *> string "encryption")
*> extendSig symEncMaudeSig
, try (string "asymmetric" *> kw MINUS *> string "encryption")
*> extendSig asymEncMaudeSig
, try (string "signing")
*> extendSig signatureMaudeSig
, string "hashing"
*> extendSig hashMaudeSig
]
functions :: Parser ()
functions =
string "functions" *> kw COLON *> sepBy1 functionSymbol (kw COMMA) *> pure ()
where
functionSymbol = do
funsym <- (,) <$> (BC.pack <$> identifier) <*> (kw SLASH *> integer)
sig <- getState
case lookup (fst funsym) (S.toList $ allFunctionSymbols sig) of
Just k | k /= snd funsym ->
fail $ "conflicting arities " ++
show k ++ " and " ++ show (snd funsym) ++
" for `" ++ BC.unpack (fst funsym)
_ -> setState (addFunctionSymbol funsym sig)
equations :: Parser ()
equations =
string "equations" *> kw COLON *> sepBy1 equation (kw COMMA) *> pure ()
where
equation = do
rrule <- RRule <$> term llit <*> (kw EQUAL *> term llit)
case rRuleToStRule rrule of
Just str ->
modifyState (addStRule str)
Nothing ->
fail $ "Not a subterm rule: " ++ show rrule
------------------------------------------------------------------------------
-- Parsing Theories
------------------------------------------------------------------------------
-- | Parse a theory.
theory :: [String] -- ^ Defined flags.
-> Parser OpenTheory
theory flags0 = do
string "theory"
thyId <- identifier
string "begin"
*> addItems (S.fromList flags0) (set thyName thyId defaultOpenTheory)
<* string "end"
where
addItems :: S.Set String -> OpenTheory -> Parser OpenTheory
addItems flags thy = asum
[ do fresh <- globallyFresh
addItems flags $
modify (sigpUniqueInsts . thySignature) (S.union fresh) thy
, do builtins
msig <- getState
addItems flags $ set (sigpMaudeSig . thySignature) msig thy
, do functions
msig <- getState
addItems flags $ set (sigpMaudeSig . thySignature) msig thy
, do equations
msig <- getState
addItems flags $ set (sigpMaudeSig . thySignature) msig thy
-- , do thy' <- foldM liftedAddProtoRule thy =<< transferProto
-- addItems flags thy'
, do thy' <- liftedAddLemma thy =<< lemma
addItems flags thy'
, do ru <- protoRule
thy' <- liftedAddProtoRule thy ru
addItems flags thy'
, do r <- intrRule
addItems flags (addIntrRuleACs [r] thy)
, do c <- formalComment
addItems flags (addFormalComment c thy)
, do ifdef flags thy
, do define flags thy
, do return thy
]
define :: S.Set String -> OpenTheory -> Parser OpenTheory
define flags thy = do
flag <- try (kw SHARP *> string "define") *> identifier
addItems (S.insert flag flags) thy
ifdef :: S.Set String -> OpenTheory -> Parser OpenTheory
ifdef flags thy = do
flag <- try (kw SHARP *> string "ifdef") *> identifier
thy' <- addItems flags thy
try (kw SHARP *> string "endif")
if flag `S.member` flags
then addItems flags thy'
else addItems flags thy
liftedAddProtoRule thy ru = case addProtoRule ru thy of
Just thy' -> return thy'
Nothing -> fail $ "duplicate rule: " ++ render (prettyRuleName ru)
liftedAddLemma thy l = case addLemma l thy of
Just thy' -> return thy'
Nothing -> fail $ "duplicate lemma: " ++ get lName l