cpsa-2.3.2: src/unsorted.pl
% -*- mode: prolog -*-
%% Unsorted CPSA terms
%% Copyright (c) 2013 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(unsorted, [to_unsorted/3, from_unsorted/3,
unify/3, unify/4, subst/3]).
:- use_module(cpsa).
%% An unsorted term has inclusion operations inserted to preserve sort
%% constraints within an unsorted algebra. It also replaces the privk
%% operation in favor of invk o pubk.
%% Term ::= Tag | Var | name(Var) | text(Var) | data(Var) | akey(Akey)
%% | skey(Skey) | cat(Term, Term) | enc(Term, Term) | hash(Term).
%% Akey ::= Var | invk(Akey) | pubk(Var) | pubk(Tag, Var).
%% Skey ::= Var | ltk(Var, Var).
%% to_unsorted(+Decls, +Term, -Uterm)
to_unsorted(Decls, Term, Uterm) :-
% Add inclusion operation to each variable that needs one
decls_as_pairs(Decls, VarMap),
to_unsorted_term(VarMap, Term, Uterm).
% Note that the operation privk is eliminated in favor of invk o pubk.
to_unsorted_term(_, Term, Term) :-
string(Term), % Case of tags
!.
to_unsorted_term(VarMap, Term, Uterm) :-
atom(Term), % Case of variables
lookup(Term, VarMap, Uterm).
to_unsorted_term(VarMap, invk(Term), akey(invk(Uterm))) :-
to_unsorted_term(VarMap, Term, akey(Uterm)).
to_unsorted_term(VarMap, pubk(Term), akey(pubk(Uterm))) :-
to_unsorted_term(VarMap, Term, name(Uterm)).
to_unsorted_term(VarMap, pubk(Term0, Term1), akey(pubk(Uterm0, Uterm1))) :-
string(Uterm0), % Case of pubk(tag, name var)
to_unsorted_term(VarMap, Term0, Uterm0),
to_unsorted_term(VarMap, Term1, name(Uterm1)).
to_unsorted_term(VarMap, privk(Term), Uterm) :-
to_unsorted_term(VarMap, invk(pubk(Term)), Uterm).
to_unsorted_term(VarMap, privk(Term0, Term1), Uterm) :-
to_unsorted_term(VarMap, invk(pubk(Term0, Term1)), Uterm).
to_unsorted_term(VarMap, ltk(Term0, Term1), skey(ltk(Uterm0, Uterm1))) :-
to_unsorted_term(VarMap, Term0, name(Uterm0)),
to_unsorted_term(VarMap, Term1, name(Uterm1)).
to_unsorted_term(VarMap, cat(Term0, Term1), cat(Uterm0, Uterm1)) :-
to_unsorted_term(VarMap, Term0, Uterm0),
to_unsorted_term(VarMap, Term1, Uterm1).
to_unsorted_term(VarMap, enc(Term0, Term1), enc(Uterm0, Uterm1)) :-
to_unsorted_term(VarMap, Term0, Uterm0),
to_unsorted_term(VarMap, Term1, Uterm1).
to_unsorted_term(VarMap, hash(Term), hash(Uterm)) :-
to_unsorted_term(VarMap, Term, Uterm).
% Adds inclusion operations to variables other than those of sort mesg.
decls_as_pairs([], []).
decls_as_pairs([d(Sort, Vars)|Decls], VarMap) :-
decl_as_pairs(Vars, Sort, Decls, VarMap).
decl_as_pairs([], _, Decls, VarMap) :-
decls_as_pairs(Decls, VarMap).
decl_as_pairs(_, mesg, Decls, VarMap) :-
!,
decls_as_pairs(Decls, VarMap).
decl_as_pairs([Var|Vars], Sort, Decls, [(Var, Term)|VarMap]) :-
Term =.. [Sort, Var],
decl_as_pairs(Vars, Sort, Decls, VarMap).
% Lookup value in an association list
lookup(Key, [(Key, Value)|_], Value) :-
!.
lookup(Key, [_|Map], Value) :-
lookup(Key, Map, Value).
% From unsorted translates back to the order-sorted algebra
% representation and also produces the appropriate variable
% declaration.
%% from_unsorted(+Uterm, -Term, -Decls).
from_unsorted(InTerm, OutTerm, Decls) :-
from_unsorted_term(InTerm, [], OutTerm, Map),
pairs_as_decls(Map, [], Decls).
% Convert an association list representation to a list of declarations.
pairs_as_decls([], Decls, Decls).
% Case when sort decls has already been constructed
pairs_as_decls([(_, Value)|Map], Decls, Decls) :-
member(d(Value, _), Decls),
!,
pairs_as_decls(Map, Decls, Decls).
% Construct one sort decl
pairs_as_decls([(Var, Value)|Map], Decls, [d(Value, [Var|Vars])|Decls]) :-
select(Value, Map, Vars).
% Collect vars of a given sort.
select(_, [], []).
select(Value, [(Var, Value)|Map], [Var|Vars]) :-
!,
select(Value, Map, Vars).
select(Value, [_|Map], Vars) :-
select(Value, Map, Vars).
%% The core of the translation is here.
%% from_unsorted_term(+InTerm, +InMap, -OutTerm, -OutMap).
from_unsorted_term(Term, Map, Term, Map) :-
string(Term), % Tags
!.
from_unsorted_term(Term, InMap, Term, OutMap) :-
atom(Term), % Variables of sort mesg
extend_map(Term, mesg, InMap, OutMap).
from_unsorted_term(name(Term), InMap, Term, OutMap) :-
atom(Term), % Variables of sort name
extend_map(Term, name, InMap, OutMap).
from_unsorted_term(text(Term), InMap, Term, OutMap) :-
atom(Term), % Variables of sort text
extend_map(Term, text, InMap, OutMap).
from_unsorted_term(data(Term), InMap, Term, OutMap) :-
atom(Term), % Variables of sort data
extend_map(Term, data, InMap, OutMap).
from_unsorted_term(skey(InTerm), InMap, OutTerm, OutMap) :-
from_unsorted_skey_term(InTerm, InMap, OutTerm, OutMap).
from_unsorted_term(akey(InTerm), InMap, OutTerm, OutMap) :-
from_unsorted_akey_term(InTerm, InMap, OutTerm, OutMap).
from_unsorted_term(cat(InTerm0, InTerm1), InMap, % Pairs
cat(OutTerm0, OutTerm1), OutMap) :-
from_unsorted_term(InTerm0, InMap, OutTerm0, Map),
from_unsorted_term(InTerm1, Map, OutTerm1, OutMap).
from_unsorted_term(enc(InTerm0, InTerm1), InMap, % Encryption
enc(OutTerm0, OutTerm1), OutMap) :-
from_unsorted_term(InTerm0, InMap, OutTerm0, Map),
from_unsorted_term(InTerm1, Map, OutTerm1, OutMap).
% Terms of sort skey
from_unsorted_skey_term(Term, InMap, Term, OutMap) :-
atom(Term), % Variables of sort skey
extend_map(Term, skey, InMap, OutMap).
% ltk: long term keys
from_unsorted_skey_term(ltk(InTerm0, InTerm1), InMap,
ltk(OutTerm0, OutTerm1), OutMap) :-
from_unsorted_term(name(InTerm0), InMap, OutTerm0, Map),
from_unsorted_term(name(InTerm1), Map, OutTerm1, OutMap).
% Terms of sort akey
from_unsorted_akey_term(Term, InMap, Term, OutMap) :-
atom(Term), % Variables of sort akey
extend_map(Term, akey, InMap, OutMap).
from_unsorted_akey_term(invk(pubk(InTerm)), InMap, privk(OutTerm), OutMap) :-
!, % privk
from_unsorted_term(name(InTerm), InMap, OutTerm, OutMap).
from_unsorted_akey_term(pubk(InTerm), InMap, pubk(OutTerm), OutMap) :-
from_unsorted_term(name(InTerm), InMap, OutTerm, OutMap). % pubk
from_unsorted_akey_term(invk(InTerm), InMap, invk(OutTerm), OutMap) :-
from_unsorted_akey_term(InTerm, InMap, OutTerm, OutMap). % invk
% Extend a mapping or fail with an inconsistent mapping.
%% extend_map(+Key, +Value, +Map, -NewMap).
extend_map(Key, Value, Map, Map) :-
lookup(Key, Map, Result),
!,
Result = Value. % Fails when key is mapped to another value
% Extend Map when Key is not mapped as long as var does occur in term
extend_map(Key, Value, Map, [(Key, Value)|Map]) :-
\+occurs(Key, Value).
occurs(Var, Var).
occurs(Var, Term) :-
Term =.. [_|Terms],
occurs_list(Var, Terms).
occurs_list(Var, [Term|_]) :-
occurs(Var, Term),
!.
occurs_list(Var, [_|Terms]) :-
occurs_list(Var, Terms).
%% Unification of unsorted terms given the equation invk(invk(k)) = k.
%% unify(+Term0, +Term1, -Subst).
unify(Term0, Term1, Subst) :-
unify(Term0, Term1, [], Subst).
%% This version extends a substition.
%% unify(+Term0, +Term1, +InSubst, -OutSubst).
unify(Term0, Term1, InSubst, OutSubst) :-
chase(Term0, InSubst, Term2),
chase(Term1, InSubst, Term3),
unify_aux(Term2, Term3, InSubst, OutSubst).
%% Find the canonical variable within an equivalence class.
%% This version of the chase handles the equation invk(invk(k)) = k.
chase(Term, _, Term) :-
string(Term), % Tag
!.
chase(InTerm, Subst, OutTerm) :-
atom(InTerm), % Variable
!,
chase_var(InTerm, Subst, OutTerm).
chase(invk(InTerm), Subst, OutTerm) :-
!,
chase_invk(InTerm, Subst, OutTerm).
chase(Term, _, Term).
chase_var(InTerm, Subst, OutTerm) :-
lookup(InTerm, Subst, Term),
!,
chase(Term, Subst, OutTerm).
chase_var(Term, _, Term).
chase_invk(Term, _, Term) :-
string(Term), % This should never happen
!.
chase_invk(InTerm, Subst, OutTerm) :-
atom(InTerm),
!,
chase_invk_var(InTerm, Subst, OutTerm).
chase_invk(invk(InTerm), Subst, OutTerm) :-
!,
chase(InTerm, Subst, OutTerm).
chase_invk(Term, _, invk(Term)).
chase_invk_var(InTerm, Subst, OutTerm) :-
lookup(InTerm, Subst, Term),
!,
chase_invk(Term, Subst, OutTerm).
chase_invk_var(Term, _, invk(Term)).
%% The remainder of unification assumes that the chase has been
%% applied to the two terms.
unify_aux(Term, Term, Subst, Subst).
unify_aux(Term0, _, Subst, Subst) :-
string(Term0),
!,
fail.
unify_aux(Term0, Term1, InSubst, OutSubst) :-
atom(Term0),
!,
extend_map(Term0, Term1, InSubst, OutSubst).
unify_aux(Term0, Term1, InSubst, OutSubst) :-
atom(Term1),
!,
unify_aux(Term1, Term0, InSubst, OutSubst).
%% Clauses that handle the equation
unify_aux(invk(Term0), pubk(Term1), InSubst, OutSubst) :-
unify_aux(Term0, invk(pubk(Term1)), InSubst, OutSubst).
unify_aux(pubk(Term0), invk(Term1), InSubst, OutSubst) :-
unify_aux(Term1, invk(pubk(Term0)), InSubst, OutSubst).
%% Clause that handles other function symbols
unify_aux(Term0, Term1, InSubst, OutSubst) :-
Term0 =.. [Fun|Terms0],
Term1 =.. [Fun|Terms1],
unify_list(Terms0, Terms1, InSubst, OutSubst).
unify_list([], [], Subst, Subst).
unify_list([Term0|Terms0], [Term1|Terms1], InSubst, OutSubst) :-
unify(Term0, Term1, InSubst, Subst),
unify_list(Terms0, Terms1, Subst, OutSubst).
%% Substition
%% subst(+InTerm, +Subst, -OutTerm).
subst(InTerm, Subst, OutTerm) :-
chase(InTerm, Subst, Term),
subst_aux(Term, Subst, OutTerm).
subst_aux(Term, _, Term) :-
atom(Term),
!.
subst_aux(Term, _, Term) :-
string(Term),
!.
% Apply equation
subst_aux(invk(InTerm), Subst, OutTerm) :-
subst(InTerm, Subst, invk(OutTerm)),
!.
subst_aux(InTerm, Subst, OutTerm) :-
InTerm =.. [Fun|InTerms],
subst_list(InTerms, Subst, OutTerms),
OutTerm =.. [Fun|OutTerms].
subst_list([], _, []).
subst_list([InTerm|InTerms], Subst, [OutTerm|OutTerms]) :-
subst(InTerm, Subst, OutTerm),
subst_list(InTerms, Subst, OutTerms).