packages feed

cpsa-4.4.1: src/prover9.pl

% -*- mode: prolog -*-

%% CPSA tools in Prolog

%% Translates the output of the cpsasas program into the syntax of
%% Prover9.

%% Known to work in SWI-Prolog, but not with GNU Prolog.

%% To handle the subsort relations of the message algebra, add the
%% following axioms.

%%    all T (akey(T) -> mesg(T)).
%%    all T (skey(T) -> mesg(T)).
%%    all T (name(T) -> mesg(T)).
%%    all T (text(T) -> mesg(T)).
%%    all T (name(T) -> mesg(T)).

%% Copyright (c) 2011 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(prover9, [prover9/2]).

:- use_module(pp).
:- use_module(sexpr).

%% prover9(+In, +Out) Translates cpsasas program output in file In,
%% into the syntax of Prover9 and places it in file Out.
prover9(In, Out) :-
	sexpr:read_sexpr_list(In, Forms),
	open(Out, write, Stream),
	top_forms_pp(Stream, Forms),
	close(Stream).

%% Ignore all forms except the ones that start with forall.
top_forms_pp(_, []).
top_forms_pp(Out, [[defgoal, _, [forall|Rest]]|Forms]) :-
	!,
	form_to_pretty([forall|Rest], Pretty),
	pp:pr(Out, 72, Pretty),
	put_char(Out, '.'),
	nl(Out),
	nl(Out),
	top_forms_pp(Out, Forms).
top_forms_pp(Out, [_|Forms]) :-
	top_forms_pp(Out, Forms).

%% Formula classification

form_to_pretty([equal|Terms], Pretty) :-
	!,
	equal(Terms, Pretty).
form_to_pretty([implies|Forms], Pretty) :-
	!,
	implication(Forms, Pretty).
form_to_pretty([and|Forms], Pretty) :-
	!,
	junction(and, Forms, Pretty).
form_to_pretty([or| Forms], Pretty) :-
	!,
	junction(or, Forms, Pretty).
form_to_pretty([forall|Rest], Pretty) :-
	!,
	quantifier(forall, Rest, Pretty).
form_to_pretty([exists|Rest], Pretty) :-
	!,
	quantifier(exists, Rest, Pretty).
form_to_pretty(Forms, Pretty) :-
	atomic_form(Forms, Pretty).

%% In general, breaks are before binary operators.

%% Equality

equal([L, R], Pretty) :-
	term_to_pretty(L, Left),
	pp:brk(1, Brk),
	pp:atm('= ', Op),
	term_to_pretty(R, Right),
	pp:blo(0, [Left, Brk, Op, Right], Pretty).

%% Implication

implication([H, C], Pretty) :-
	form_to_pretty(H, Hypoth),
	pp:brk(1, Brk),
	pp:atm('-> ', Op),
	form_to_pretty(C, Concl),
	pp:blo(0, [Hypoth, Brk, Op, Concl], Pretty).

%% Conjunction and disjunction

junction(and, [], Pretty) :-
	pp:atm('$T', Pretty).
junction(or, [], Pretty) :-
	pp:atm('$F', Pretty).
junction(_, [Form], Pretty) :-
	!,
	form_to_pretty(Form, Pretty).
junction(Kind, [Form|Forms], Pretty) :-
	form_to_pretty(Form, First),
	junction_op(Kind, Op),
	junction_rest(Op, Forms, Pretties),
	pp:blo(0, [First|Pretties], Pretty).

junction_op(and, Pretty) :-
	pp:atm('&', Pretty).
junction_op(or, Pretty) :-
	pp:atm('|', Pretty).

junction_rest(_, [], []).
junction_rest(Op, [Form|Forms], [Brk, Op, Sp, Next|Pretties]) :-
	pp:brk(1, Brk),
	pp:atm(' ', Sp),
	form_to_pretty(Form, Next),
	junction_rest(Op, Forms, Pretties).

%% Quantifiers forall and exists

quantifier(_, [[], Body], Pretty) :-
	!,
	form_to_pretty(Body, Pretty).
quantifier(Kind, [Decls, Body], Pretty) :-
	quantifier_op(Kind, Op),
	decls(Decls, SVars),
	quantifier_preds(SVars, Vars, Preds),
	quantifier_body(Kind, Preds, Body, SBody),
	quantifier_rest(Op, Vars, SBody, Pretties),
	pp:blo(2, Pretties, Pretty).

quantifier_op(forall, Pretty) :-
	pp:atm(all, Pretty).
quantifier_op(exists, Pretty) :-
	pp:atm(exists, Pretty).

%% Collect vars in declarations.

decls(Decls, SortedVars) :-
	decls(Decls, SortedVars, []).

decls([], SVars, SVars).
decls([Decl|Decls], SVars, End) :-
	decl(Decl, _, SVars, Middle),
	decls(Decls, Middle, End).

decl([Sort], Sort, SVars, SVars) :-
	atom(Sort).
decl([Var|Decl], Sort, [(Var,Sort)|SVars], End) :-
	atom(Var),
	decl(Decl, Sort, SVars, End).

quantifier_preds([], [], []).
quantifier_preds([(Var,Sort)|SVars], [Var|Vars], [[Sort, Var]|Preds]) :-
        quantifier_preds(SVars, Vars, Preds).

quantifier_body(_, [], Body, Body) :-
        !.
quantifier_body(exists, Preds, [and|Forms], [and|Conj]) :-
        !,
        append(Preds, Forms, Conj).
quantifier_body(exists, Preds, Body, [and|Conj]) :-
        append(Preds, [Body], Conj).
quantifier_body(forall, Preds, [implies, [and|Forms], Body],
		[implies, [and|Conj], Body]) :-
        !,
        append(Preds, Forms, Conj).
quantifier_body(forall, Preds, [implies, Form, Body],
		[implies, [and|Conj], Body]) :-
        !,
        append(Preds, [Form], Conj).
quantifier_body(forall, Preds, Body, [implies, [and|Preds], Body]).

quantifier_rest(_, [], Body, [Left, Brk, Pretty, Right]) :-
	pp:atm('(', Left),
	pp:brk(0, Brk),
	form_to_pretty(Body, Pretty),
	pp:atm(')', Right).
quantifier_rest(Op, [Var|Vars], Body, [Op, Sp, V, Brk|Pretties]) :-
	pp:atm(' ', Sp),
	term_to_pretty(Var, V),
	pp:brk(1, Brk),
	quantifier_rest(Op, Vars, Body, Pretties).

% Atomic formulas

atomic_form([Pred], Pretty) :-
	atom(Pred),
	pp:atm(Pred, Pretty).
atomic_form([Pred, Term|Terms], Pretty) :-
	atom(Pred),
	pp:atm(Pred, P),
	pp:atm('(', Left),
	term_to_pretty(Term, T),
	terms_to_pretty(Terms, Ts),
	pp:blo(2, [P, Left, T|Ts], Pretty).

terms_to_pretty([], [Right]) :-
	pp:atm(')', Right).
terms_to_pretty([Term|Terms], [Comma, Brk, T|Pretties]) :-
	pp:atm(',', Comma),
	pp:brk(1, Brk),
	term_to_pretty(Term, T),
	terms_to_pretty(Terms, Pretties).

term_to_pretty(Term, Pretty) :-
	load_term(Term, Internal),
	term_pp(Internal, Pretty).

%% Load a term using CPSA's parsing rules for terms.  Also, convert
%% variables into uppercase atoms, and constants into lowercase atoms.

load_term(Term, Internal) :-
	atom(Term),
	upcase_symbol(Term, Internal).
load_term(Term, Internal) :-
	string(Term),
	string_to_atom(Term, Atom),
	downcase_symbol(Atom, Internal).
load_term(Term, Term) :-
	integer(Term),
	Term >= 0.
load_term([privk|Terms], Internals) :-
	!,
	load_term([invk, [pubk|Terms]], Internals).
load_term([cat,Term], Internals) :-
	!,
	load_term(Term, Internals).
load_term([cat,Term|Terms], [cat, X, Y]) :-
	!,
	load_term(Term, X),
	load_term([cat|Terms], Y).
load_term([enc|Terms], Internal) :-
	!,
	load_enc(Terms, Internal).
load_term([Term|Terms], [Term|Internals]) :-
	atom(Term),
	load_terms(Terms, Internals).

upcase_symbol(Atom, Symbol) :-
	atom_chars(Atom, Chars),
	upcase_parts(Chars, Parts),
	atom_chars(Symbol, Parts).

upcase_parts([First|Rest], [Lead|Tail]) :-
	upcase_atom(First, Lead),
	symbol_parts(Rest, Tail).

downcase_symbol(Atom, Symbol) :-
	atom_chars(Atom, Chars),
	downcase_parts(Chars, Parts),
	atom_chars(Symbol, Parts).

%% The null constant is used for the name of the listener role.

downcase_parts([], ['[', ']']).
downcase_parts([First|Rest], [Lead|Tail]) :-
	downcase_atom(First, Lead),
	symbol_parts(Rest, Tail).

symbol_parts([], []).
symbol_parts(['-'|Rest], ['_'|Tail]) :-
	!,
	symbol_parts(Rest, Tail).
symbol_parts([First|Rest], [First|Tail]) :-
	symbol_parts(Rest, Tail).

load_enc(Terms, [enc, X, Y]) :-
	split(Terms, As, B),
	load_term([cat|As], X),
	load_term(B, Y).

split([X], [], X).
split([X, Y|Z], [X|A], B) :-
	split([Y|Z], A, B).

load_terms([], []).
load_terms([T|Ts], [I|Is]) :-
	load_term(T, I),
	load_terms(Ts, Is).

%% Pretty print a term.

term_pp(Term, Pretty) :-
	atom(Term),
	pp:atm(Term, Pretty).
term_pp([Term], Pretty) :-
	atom(Term),
	pp:atm(Term, Pretty).
term_pp([Pred, Term|Terms], Pretty) :-
	atom(Pred),
	pp:atm(Pred, P),
	pp:atm('(', Left),
	term_pp(Term, Arg),
	args_pp(Terms, Pretties),
	blo(2, [P, Left, Arg|Pretties], Pretty).
%% Natural numbers are turned into lists of nulls.
%% The number is the length of the list.
term_pp(0, Pretty) :-
	pp:atm('[]', Pretty).
term_pp(Term, Pretty) :-
	integer(Term),
	succ(Num, Term),
	pp:atm('[[]', First),
	nat_pp(Num, Pretties),
	blo(1, [First|Pretties], Pretty).

args_pp([], [Pretty]) :-
	pp:atm(')', Pretty).
args_pp([Term|Terms], [Comma, Brk, Pretty|Pretties]) :-
	pp:atm(',', Comma),
	pp:brk(1, Brk),
	term_pp(Term, Pretty),
	args_pp(Terms, Pretties).

nat_pp(0, [Right]) :-
	!,
	pp:atm(']', Right).
nat_pp(Num, [Comma, Brk, Nil|Pretties]) :-
	succ(Num1, Num),
	pp:atm(',', Comma),
	pp:brk(1, Brk),
	pp:atm('[]', Nil),
	nat_pp(Num1, Pretties).