scyther/src/terms.c
2004-05-15 12:33:01 +00:00

724 lines
14 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "terms.h"
#include "debug.h"
#include "memory.h"
#include "ctype.h"
/* external definitions */
extern Term TERM_Function;
extern int inTermlist (); // suppresses a warning, but at what cost?
extern int globalLatex;
/* forward declarations */
void indent (void);
/* useful macros */
#define RID_UNDEF MIN_INT
/* main code */
/* Two types of terms: general, and normalized. Normalized rewrites all
tuples to (x,(y,z))..NULL form, making list traversal easy. */
//! Initialization of terms code.
void
termsInit (void)
{
return;
}
//! Cleanup of terms code.
void
termsDone (void)
{
return;
}
//! Allocate memory for a term.
/**
*@return A pointer to the new term memory, which is not yet initialised.
*/
Term
makeTerm ()
{
return (Term) memAlloc (sizeof (struct term));
}
//! Create a fresh encrypted term from two existing terms.
/**
*@return A pointer to the new term.
*/
Term
makeTermEncrypt (Term t1, Term t2)
{
Term term = makeTerm ();
term->type = ENCRYPT;
term->stype = NULL;
term->op = t1;
term->key = t2;
return term;
}
//! Create a fresh term tuple from two existing terms.
/**
*@return A pointer to the new term.
*/
Term
makeTermTuple (Term t1, Term t2)
{
if (t1 == NULL)
{
if (t2 == NULL)
{
#ifdef DEBUG
debug (5, "Trying to make a tuple node with an empty term.");
#endif
return NULL;
}
else
return t2;
}
if (t2 == NULL)
{
return t1;
}
Term tt = makeTerm ();
tt->type = TUPLE;
tt->stype = NULL;
tt->op1 = t1;
tt->op2 = t2;
return tt;
}
//! Make a term of the given type with run identifier and symbol.
/**
*@return A pointer to the new term.
*\sa GLOBAL, VARIABLE, LEAF, ENCRYPT, TUPLE
*/
Term
makeTermType (const int type, const Symbol symb, const int runid)
{
Term term = makeTerm ();
term->type = type;
term->stype = NULL;
term->subst = NULL;
term->symb = symb;
term->runid = runid;
return term;
}
//! Unwrap any substitutions.
/**
* For speed, it is also a macro. Sometimes it will call
* deVarScan to do the actual unwinding.
*@return A term that is either not a variable, or has a NULL substitution.
*\sa deVar()
*/
Term
deVarScan (Term t)
{
while (realTermVariable (t) && t->subst != NULL)
t = t->subst;
return t;
}
//! Determine whether a term contains an unsubstituted variable as subterm.
/**
*@return True iff there is an open variable as subterm.
*/
int
hasTermVariable (Term term)
{
if (term == NULL)
return 0;
term = deVar (term);
if (realTermLeaf (term))
return realTermVariable (term);
else
{
if (realTermTuple (term))
return (hasTermVariable (term->op1) || hasTermVariable (term->op2));
else
return (hasTermVariable (term->op) || hasTermVariable (term->key));
}
}
//!Tests whether two terms are completely identical.
/**
* This also includes
* variables. This is the recursive function.
* We assume the term is normalized, e.g. no tupling has direct
* subtupling.
*@return True iff the terms are equal.
*\sa isTermEqual()
*/
int
isTermEqualFn (Term term1, Term term2)
{
term1 = deVar (term1);
term2 = deVar (term2);
if (term1 == term2)
return 1;
if ((term1 == NULL) || (term2 == NULL))
return 0;
if (term1->type != term2->type)
{
return 0;
}
if (realTermLeaf (term1))
{
return (term1->symb == term2->symb && term1->runid == term2->runid);
}
else
{
/* ENCRYPT or TUPLE */
if (realTermEncrypt (term1))
{
/* for optimization of encryption equality, we compare
operator 2 first (we expect it to be a smaller term)
*/
return (isTermEqualFn (term1->key, term2->key) &&
isTermEqualFn (term1->op, term2->op));
}
else
{
/* tuple */
return (isTermEqualFn (term1->op1, term2->op1) &&
isTermEqualFn (term1->op2, term2->op2));
}
}
}
//! See if a term is a subterm of another.
/**
*@param t Term to be checked for a subterm.
*@param tsub Subterm.
*@return True iff tsub is a subterm of t.
*/
int
termOccurs (Term t, Term tsub)
{
t = deVar (t);
tsub = deVar (tsub);
if (isTermEqual (t, tsub))
return 1;
if (realTermLeaf (t))
return 0;
if (realTermTuple (t))
return (termOccurs (t->op1, tsub) || termOccurs (t->op2, tsub));
else
return (termOccurs (t->op, tsub) || termOccurs (t->key, tsub));
}
//! Print a term to stdout.
void
termPrint (Term term)
{
if (term == NULL)
{
printf ("Empty term");
return;
}
#ifdef DEBUG
if (!DEBUGL (1))
{
term = deVar (term);
}
#else
term = deVar (term);
#endif
if (realTermLeaf (term))
{
symbolPrint (term->symb);
if (realTermVariable (term))
printf ("V");
if (term->runid >= 0)
{
if (globalLatex)
printf ("\\sharp%i", term->runid);
else
printf ("#%i", term->runid);
}
if (term->subst != NULL)
{
if (globalLatex)
printf ("\\rightarrow");
else
printf ("->");
termPrint (term->subst);
}
}
if (realTermTuple (term))
{
printf ("(");
while (realTermTuple (term))
{
termPrint (term->op1);
printf (",");
term = term->op2;
if (!realTermTuple (term))
termPrint (term);
}
printf (")");
return;
}
if (realTermEncrypt (term))
{
if (isTermLeaf (term->key)
&& inTermlist (term->key->stype, TERM_Function))
{
/* function application */
termPrint (term->key);
printf ("(");
termPrint (term->op);
printf (")");
}
else
{
/* normal encryption */
if (globalLatex)
{
printf ("\\{");
termPrint (term->op);
printf ("\\}_{");
termPrint (term->key);
printf ("}");
}
else
{
printf ("{");
termPrint (term->op);
printf ("}");
termPrint (term->key);
}
}
}
}
//! Make a deep copy of a term.
/**
* Leaves are not copied.
*@return If the original was a leaf, then the pointer is simply returned. Otherwise, new memory is allocated and the node is copied recursively.
*\sa termDuplicateDeep()
*/
Term
termDuplicate (const Term term)
{
Term newterm;
if (term == NULL)
return NULL;
if (realTermLeaf (term))
return term;
newterm = (Term) memAlloc (sizeof (struct term));
newterm->type = term->type;
if (realTermEncrypt (term))
{
newterm->op = termDuplicate (term->op);
newterm->key = termDuplicate (term->key);
}
else
{
newterm->op1 = termDuplicate (term->op1);
newterm->op2 = termDuplicate (term->op2);
}
return newterm;
}
//! Make a true deep copy of a term.
/**
* Currently, it this function is not to be used, so we can be sure leaf nodes occur only once in the system.
*@return New memory is allocated and the node is copied recursively.
*\sa termDuplicate()
*/
Term
termDuplicateDeep (const Term term)
{
Term newterm;
if (term == NULL)
return NULL;
newterm = (Term) memAlloc (sizeof (struct term));
if (realTermLeaf (term))
{
memcpy (newterm, term, sizeof (struct term));
}
else
{
newterm->type = term->type;
if (realTermEncrypt (term))
{
newterm->op = termDuplicateDeep (term->op);
newterm->key = termDuplicateDeep (term->key);
}
else
{
newterm->op1 = termDuplicateDeep (term->op1);
newterm->op2 = termDuplicateDeep (term->op2);
}
}
return newterm;
}
//! Make a copy of a term, but remove substituted variable nodes.
/**
* Remove all instantiated variables on the way down.
*\sa termDuplicate()
*/
Term
termDuplicateUV (Term term)
{
Term newterm;
if (term == NULL)
return NULL;
term = deVar (term);
if (realTermLeaf (term))
return term;
newterm = (Term) memAlloc (sizeof (struct term));
newterm->type = term->type;
if (realTermEncrypt (term))
{
newterm->op = termDuplicateUV (term->op);
newterm->key = termDuplicateUV (term->key);
}
else
{
newterm->op1 = termDuplicateUV (term->op1);
newterm->op2 = termDuplicateUV (term->op2);
}
return newterm;
}
/*
realTermDuplicate
make a deep copy of a term, also of leaves.
*/
Term
realTermDuplicate (const Term term)
{
Term newterm;
if (term == NULL)
return NULL;
newterm = (Term) memAlloc (sizeof (struct term));
if (realTermLeaf (term))
{
memcpy (newterm, term, sizeof (struct term));
}
else
{
newterm->type = term->type;
if (realTermEncrypt (term))
{
newterm->op = realTermDuplicate (term->op);
newterm->key = realTermDuplicate (term->key);
}
else
{
newterm->op1 = realTermDuplicate (term->op1);
newterm->op2 = realTermDuplicate (term->op2);
}
}
return newterm;
}
//!Removes a term and deallocates memory.
/**
* Is meant to remove terms make with termDuplicate. Only deallocates memory
* of nodes, not of leaves.
*\sa termDuplicate(), termDuplicateUV()
*/
void
termDelete (const Term term)
{
if (term != NULL && !realTermLeaf (term))
{
if (realTermEncrypt (term))
{
termDelete (term->op);
termDelete (term->key);
}
else
{
termDelete (term->op1);
termDelete (term->op2);
}
memFree (term, sizeof (struct term));
}
}
//! Normalize a term with respect to tupling.
/**
* Avoids problems with associativity by rewriting every ((x,y),z) to
* (x,(y,z)), i.e. a normal form for terms, after which equality is
* okay. No memory was allocated or deallocated, as only pointers are swapped.
*
*@return After execution, the term pointed at has been normalized. */
void
termNormalize (Term term)
{
term = deVar (term);
if (term == NULL || realTermLeaf (term))
return;
if (realTermEncrypt (term))
{
termNormalize (term->op);
termNormalize (term->key);
}
else
{
/* normalize left hand first,both for tupling and for
encryption */
termNormalize (term->op1);
/* check for ((x,y),z) construct */
if (realTermTuple (term->op1))
{
/* temporarily store the old terms */
Term tx = (term->op1)->op1;
Term ty = (term->op1)->op2;
Term tz = term->op2;
/* move node */
term->op2 = term->op1;
/* construct (x,(y,z)) version */
term->op1 = tx;
(term->op2)->op1 = ty;
(term->op2)->op2 = tz;
}
termNormalize (term->op2);
}
}
//! Copy a term, and ensure all run identifiers are set to the new value.
/**
* Strange code. Only to be used on locals, as is stupidly replaces all run identifiers.
*@return The new term.
*\sa termDuplicate()
*/
Term
termRunid (Term term, int runid)
{
if (term == NULL)
return NULL;
if (realTermLeaf (term))
{
/* leaf */
if (term->runid == runid)
return term;
else
{
Term newt = termDuplicate (term);
newt->runid = runid;
return newt;
}
}
else
{
/* anything else, recurse */
if (realTermEncrypt (term))
{
return makeTermEncrypt (termRunid (term->op, runid),
termRunid (term->key, runid));
}
else
{
return makeTermTuple (termRunid (term->op1, runid),
termRunid (term->op2, runid));
}
}
}
//! Determine tuple width of a given term.
/**
*\sa tupleProject()
*/
int
tupleCount (Term tt)
{
if (tt == NULL)
{
return 0;
}
else
{
deVar (tt);
if (!realTermTuple (tt))
{
return 1;
}
else
{
return (tupleCount (tt->op1) + tupleCount (tt->op2));
}
}
}
//! Yield the projection Pi(n) of a term.
/**
*@param tt Term
*@param n The index in the tuple.
*@return Returns either a pointer to a term, or NULL if the index is out of range.
*\sa tupleCount()
*/
Term
tupleProject (Term tt, int n)
{
if (tt == NULL)
{
return NULL;
}
deVar (tt);
if (!realTermTuple (tt))
{
if (n > 0)
{
/* no tuple, adressing error */
return NULL;
}
else
{
/* no tuple */
return tt;
}
}
else
{
/* there is a tuple to traverse */
int left = tupleCount (tt->op1);
if (n >= left)
{
/* it's in the right hand side */
return tupleProject (tt->op2, n - left);
}
else
{
/* left hand side */
return tupleProject (tt->op1, n);
}
}
}
//! Determine size of term.
/**
* Determines the size of a term according to some heuristic.
* Currently, the encryption operator is weighed as well.
*@return Returns a nonnegative integer.
*\sa termDistance()
*/
int
termSize(Term t)
{
if (t == NULL)
{
return 0;
}
t = deVar(t);
if (realTermLeaf(t))
{
return 1;
}
else
{
if (realTermEncrypt(t))
{
return 1 + termSize(t->op) + termSize(t->key);
}
else
{
return termSize(t->op1) + termSize(t->op2);
}
}
}
//! Determine distance between two terms.
/**
*@return A float value between 0, completely dissimilar, and 1, equal.
*\sa termSize()
*/
float
termDistance(Term t1, Term t2)
{
/* First the special cases: no equal subterms, completely equal */
if (isTermEqual(t1,t2))
return 1;
t1 = deVar(t1);
t2 = deVar(t2);
int t1s = termSize(t1);
int t2s = termSize(t2);
if (t1 == NULL || t2 == NULL)
{
return 0;
}
if (t1->type != t2->type)
{
/* unequal type, maybe one is a subterm of the other? */
if (t1s > t2s && termOccurs(t1,t2))
{
return (float) t2s / t1s;
}
if (t2s > t1s && termOccurs(t2,t1))
{
return (float) t1s / t2s;
}
return 0;
}
else
{
/* equal types */
if (isTermLeaf(t1))
{
/* we had established before that they are not equal */
return 0;
}
else
{
/* non-leaf recurse */
if (isTermEncrypt(t1))
{
/* encryption */
return (termDistance(t1->op, t2->op) + termDistance(t1->key, t2->key)) / 2;
}
else
{
return (termDistance(t1->op1, t2->op1) + termDistance(t1->op2, t2->op2)) / 2;
}
}
}
}