/***** tl_spin: tl_trans.c *****/
/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
/* All Rights Reserved. This software is for educational purposes only. */
/* No guarantee whatsoever is expressed or implied by the distribution of */
/* this code. Permission is given to distribute this code provided that */
/* this introductory message is not removed and no monies are exchanged. */
/* Software written by Gerard J. Holzmann. For tool documentation see: */
/* http://spinroot.com/ */
/* Send all bug-reports and/or questions to: bugs@spinroot.com */
/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
#include "tl.h"
extern FILE *tl_out;
extern int tl_errs, tl_verbose, tl_terse, newstates, state_cnt;
int Stack_mx=0, Max_Red=0, Total=0;
static Mapping *Mapped = (Mapping *) 0;
static Graph *Nodes_Set = (Graph *) 0;
static Graph *Nodes_Stack = (Graph *) 0;
static char dumpbuf[2048];
static int Red_cnt = 0;
static int Lab_cnt = 0;
static int Base = 0;
static int Stack_sz = 0;
static Graph *findgraph(char *);
static Graph *pop_stack(void);
static Node *Duplicate(Node *);
static Node *flatten(Node *);
static Symbol *catSlist(Symbol *, Symbol *);
static Symbol *dupSlist(Symbol *);
static char *newname(void);
static int choueka(Graph *, int);
static int not_new(Graph *);
static int set_prefix(char *, int, Graph *);
static void Addout(char *, char *);
static void fsm_trans(Graph *, int, char *);
static void mkbuchi(void);
static void expand_g(Graph *);
static void fixinit(Node *);
static void liveness(Node *);
static void mk_grn(Node *);
static void mk_red(Node *);
static void ng(Symbol *, Symbol *, Node *, Node *, Node *);
static void push_stack(Graph *);
static void sdump(Node *);
void
ini_trans(void)
{
Stack_mx = 0;
Max_Red = 0;
Total = 0;
Mapped = (Mapping *) 0;
Nodes_Set = (Graph *) 0;
Nodes_Stack = (Graph *) 0;
memset(dumpbuf, 0, sizeof(dumpbuf));
Red_cnt = 0;
Lab_cnt = 0;
Base = 0;
Stack_sz = 0;
}
static void
dump_graph(Graph *g)
{ Node *n1;
printf("\n\tnew:\t");
for (n1 = g->New; n1; n1 = n1->nxt)
{ dump(n1); printf(", "); }
printf("\n\told:\t");
for (n1 = g->Old; n1; n1 = n1->nxt)
{ dump(n1); printf(", "); }
printf("\n\tnxt:\t");
for (n1 = g->Next; n1; n1 = n1->nxt)
{ dump(n1); printf(", "); }
printf("\n\tother:\t");
for (n1 = g->Other; n1; n1 = n1->nxt)
{ dump(n1); printf(", "); }
printf("\n");
}
static void
push_stack(Graph *g)
{
if (!g) return;
g->nxt = Nodes_Stack;
Nodes_Stack = g;
if (tl_verbose)
{ Symbol *z;
printf("\nPush %s, from ", g->name->name);
for (z = g->incoming; z; z = z->next)
printf("%s, ", z->name);
dump_graph(g);
}
Stack_sz++;
if (Stack_sz > Stack_mx) Stack_mx = Stack_sz;
}
static Graph *
pop_stack(void)
{ Graph *g = Nodes_Stack;
if (g) Nodes_Stack = g->nxt;
Stack_sz--;
return g;
}
static char *
newname(void)
{ static char buf[32];
sprintf(buf, "S%d", state_cnt++);
return buf;
}
static int
has_clause(int tok, Graph *p, Node *n)
{ Node *q, *qq;
switch (n->ntyp) {
case AND:
return has_clause(tok, p, n->lft) &&
has_clause(tok, p, n->rgt);
case OR:
return has_clause(tok, p, n->lft) ||
has_clause(tok, p, n->rgt);
}
for (q = p->Other; q; q = q->nxt)
{ qq = right_linked(q);
if (anywhere(tok, n, qq))
return 1;
}
return 0;
}
static void
mk_grn(Node *n)
{ Graph *p;
n = right_linked(n);
more:
for (p = Nodes_Set; p; p = p->nxt)
if (p->outgoing
&& has_clause(AND, p, n))
{ p->isgrn[p->grncnt++] =
(unsigned char) Red_cnt;
Lab_cnt++;
}
if (n->ntyp == U_OPER) /* 3.4.0 */
{ n = n->rgt;
goto more;
}
}
static void
mk_red(Node *n)
{ Graph *p;
n = right_linked(n);
for (p = Nodes_Set; p; p = p->nxt)
{ if (p->outgoing
&& has_clause(0, p, n))
{ if (p->redcnt >= 63)
Fatal("too many Untils", (char *)0);
p->isred[p->redcnt++] =
(unsigned char) Red_cnt;
Lab_cnt++; Max_Red = Red_cnt;
} }
}
static void
liveness(Node *n)
{
if (n)
switch (n->ntyp) {
#ifdef NXT
case NEXT:
liveness(n->lft);
break;
#endif
case U_OPER:
Red_cnt++;
mk_red(n);
mk_grn(n->rgt);
/* fall through */
case V_OPER:
case OR: case AND:
liveness(n->lft);
liveness(n->rgt);
break;
}
}
static Graph *
findgraph(char *nm)
{ Graph *p;
Mapping *m;
for (p = Nodes_Set; p; p = p->nxt)
if (!strcmp(p->name->name, nm))
return p;
for (m = Mapped; m; m = m->nxt)
if (strcmp(m->from, nm) == 0)
return m->to;
printf("warning: node %s not found\n", nm);
return (Graph *) 0;
}
static void
Addout(char *to, char *from)
{ Graph *p = findgraph(from);
Symbol *s;
if (!p) return;
s = getsym(tl_lookup(to));
s->next = p->outgoing;
p->outgoing = s;
}
#ifdef NXT
int
only_nxt(Node *n)
{
switch (n->ntyp) {
case NEXT:
return 1;
case OR:
case AND:
return only_nxt(n->rgt) && only_nxt(n->lft);
default:
return 0;
}
}
#endif
int
dump_cond(Node *pp, Node *r, int first)
{ Node *q;
int frst = first;
if (!pp) return frst;
q = dupnode(pp);
q = rewrite(q);
if (q->ntyp == PREDICATE
|| q->ntyp == NOT
#ifndef NXT
|| q->ntyp == OR
#endif
|| q->ntyp == FALSE)
{ if (!frst) fprintf(tl_out, " && ");
dump(q);
frst = 0;
#ifdef NXT
} else if (q->ntyp == OR)
{ if (!frst) fprintf(tl_out, " && ");
fprintf(tl_out, "((");
frst = dump_cond(q->lft, r, 1);
if (!frst)
fprintf(tl_out, ") || (");
else
{ if (only_nxt(q->lft))
{ fprintf(tl_out, "1))");
return 0;
}
}
frst = dump_cond(q->rgt, r, 1);
if (frst)
{ if (only_nxt(q->rgt))
fprintf(tl_out, "1");
else
fprintf(tl_out, "0");
frst = 0;
}
fprintf(tl_out, "))");
#endif
} else if (q->ntyp == V_OPER
&& !anywhere(AND, q->rgt, r))
{ frst = dump_cond(q->rgt, r, frst);
} else if (q->ntyp == AND)
{
frst = dump_cond(q->lft, r, frst);
frst = dump_cond(q->rgt, r, frst);
}
return frst;
}
static int
choueka(Graph *p, int count)
{ int j, k, incr_cnt = 0;
for (j = count; j <= Max_Red; j++) /* for each acceptance class */
{ int delta = 0;
/* is state p labeled Grn-j OR not Red-j ? */
for (k = 0; k < (int) p->grncnt; k++)
if (p->isgrn[k] == j)
{ delta = 1;
break;
}
if (delta)
{ incr_cnt++;
continue;
}
for (k = 0; k < (int) p->redcnt; k++)
if (p->isred[k] == j)
{ delta = 1;
break;
}
if (delta) break;
incr_cnt++;
}
return incr_cnt;
}
static int
set_prefix(char *pref, int count, Graph *r2)
{ int incr_cnt = 0; /* acceptance class 'count' */
if (Lab_cnt == 0
|| Max_Red == 0)
sprintf(pref, "accept"); /* new */
else if (count >= Max_Red)
sprintf(pref, "T0"); /* cycle */
else
{ incr_cnt = choueka(r2, count+1);
if (incr_cnt + count >= Max_Red)
sprintf(pref, "accept"); /* last hop */
else
sprintf(pref, "T%d", count+incr_cnt);
}
return incr_cnt;
}
static void
fsm_trans(Graph *p, int count, char *curnm)
{ Graph *r;
Symbol *s;
char prefix[128], nwnm[256];
if (!p->outgoing)
addtrans(p, curnm, False, "accept_all");
for (s = p->outgoing; s; s = s->next)
{ r = findgraph(s->name);
if (!r) continue;
if (r->outgoing)
{ (void) set_prefix(prefix, count, r);
sprintf(nwnm, "%s_%s", prefix, s->name);
} else
strcpy(nwnm, "accept_all");
if (tl_verbose)
{ printf("maxred=%d, count=%d, curnm=%s, nwnm=%s ",
Max_Red, count, curnm, nwnm);
printf("(greencnt=%d,%d, redcnt=%d,%d)\n",
r->grncnt, r->isgrn[0],
r->redcnt, r->isred[0]);
}
addtrans(p, curnm, r->Old, nwnm);
}
}
static void
mkbuchi(void)
{ Graph *p;
int k;
char curnm[64];
for (k = 0; k <= Max_Red; k++)
for (p = Nodes_Set; p; p = p->nxt)
{ if (!p->outgoing)
continue;
if (k != 0
&& !strcmp(p->name->name, "init")
&& Max_Red != 0)
continue;
if (k == Max_Red
&& strcmp(p->name->name, "init") != 0)
strcpy(curnm, "accept_");
else
sprintf(curnm, "T%d_", k);
strcat(curnm, p->name->name);
fsm_trans(p, k, curnm);
}
fsm_print();
}
static Symbol *
dupSlist(Symbol *s)
{ Symbol *p1, *p2, *p3, *d = ZS;
for (p1 = s; p1; p1 = p1->next)
{ for (p3 = d; p3; p3 = p3->next)
{ if (!strcmp(p3->name, p1->name))
break;
}
if (p3) continue; /* a duplicate */
p2 = getsym(p1);
p2->next = d;
d = p2;
}
return d;
}
static Symbol *
catSlist(Symbol *a, Symbol *b)
{ Symbol *p1, *p2, *p3, *tmp;
/* remove duplicates from b */
for (p1 = a; p1; p1 = p1->next)
{ p3 = ZS;
for (p2 = b; p2; p2 = p2->next)
{ if (strcmp(p1->name, p2->name))
{ p3 = p2;
continue;
}
tmp = p2->next;
tfree((void *) p2);
if (p3)
p3->next = tmp;
else
b = tmp;
} }
if (!a) return b;
if (!b) return a;
if (!b->next)
{ b->next = a;
return b;
}
/* find end of list */
for (p1 = a; p1->next; p1 = p1->next)
;
p1->next = b;
return a;
}
static void
fixinit(Node *orig)
{ Graph *p1, *g;
Symbol *q1, *q2 = ZS;
ng(tl_lookup("init"), ZS, ZN, ZN, ZN);
p1 = pop_stack();
p1->nxt = Nodes_Set;
p1->Other = p1->Old = orig;
Nodes_Set = p1;
for (g = Nodes_Set; g; g = g->nxt)
{ for (q1 = g->incoming; q1; q1 = q2)
{ q2 = q1->next;
Addout(g->name->name, q1->name);
tfree((void *) q1);
}
g->incoming = ZS;
}
}
static Node *
flatten(Node *p)
{ Node *q, *r, *z = ZN;
for (q = p; q; q = q->nxt)
{ r = dupnode(q);
if (z)
z = tl_nn(AND, r, z);
else
z = r;
}
if (!z) return z;
z = rewrite(z);
return z;
}
static Node *
Duplicate(Node *n)
{ Node *n1, *n2, *lst = ZN, *d = ZN;
for (n1 = n; n1; n1 = n1->nxt)
{ n2 = dupnode(n1);
if (lst)
{ lst->nxt = n2;
lst = n2;
} else
d = lst = n2;
}
return d;
}
static void
ng(Symbol *s, Symbol *in, Node *isnew, Node *isold, Node *next)
{ Graph *g = (Graph *) tl_emalloc(sizeof(Graph));
if (s) g->name = s;
else g->name = tl_lookup(newname());
if (in) g->incoming = dupSlist(in);
if (isnew) g->New = flatten(isnew);
if (isold) g->Old = Duplicate(isold);
if (next) g->Next = flatten(next);
push_stack(g);
}
static void
sdump(Node *n)
{
switch (n->ntyp) {
case PREDICATE: strcat(dumpbuf, n->sym->name);
break;
case U_OPER: strcat(dumpbuf, "U");
goto common2;
case V_OPER: strcat(dumpbuf, "V");
goto common2;
case OR: strcat(dumpbuf, "|");
goto common2;
case AND: strcat(dumpbuf, "&");
common2: sdump(n->rgt);
common1: sdump(n->lft);
break;
#ifdef NXT
case NEXT: strcat(dumpbuf, "X");
goto common1;
#endif
case NOT: strcat(dumpbuf, "!");
goto common1;
case TRUE: strcat(dumpbuf, "T");
break;
case FALSE: strcat(dumpbuf, "F");
break;
default: strcat(dumpbuf, "?");
break;
}
}
Symbol *
DoDump(Node *n)
{
if (!n) return ZS;
if (n->ntyp == PREDICATE)
return n->sym;
dumpbuf[0] = '\0';
sdump(n);
return tl_lookup(dumpbuf);
}
static int
not_new(Graph *g)
{ Graph *q1; Node *tmp, *n1, *n2;
Mapping *map;
tmp = flatten(g->Old); /* duplicate, collapse, normalize */
g->Other = g->Old; /* non normalized full version */
g->Old = tmp;
g->oldstring = DoDump(g->Old);
tmp = flatten(g->Next);
g->nxtstring = DoDump(tmp);
if (tl_verbose) dump_graph(g);
Debug2("\tformula-old: [%s]\n", g->oldstring?g->oldstring->name:"true");
Debug2("\tformula-nxt: [%s]\n", g->nxtstring?g->nxtstring->name:"true");
for (q1 = Nodes_Set; q1; q1 = q1->nxt)
{ Debug2(" compare old to: %s", q1->name->name);
Debug2(" [%s]", q1->oldstring?q1->oldstring->name:"true");
Debug2(" compare nxt to: %s", q1->name->name);
Debug2(" [%s]", q1->nxtstring?q1->nxtstring->name:"true");
if (q1->oldstring != g->oldstring
|| q1->nxtstring != g->nxtstring)
{ Debug(" => different\n");
continue;
}
Debug(" => match\n");
if (g->incoming)
q1->incoming = catSlist(g->incoming, q1->incoming);
/* check if there's anything in g->Other that needs
adding to q1->Other
*/
for (n2 = g->Other; n2; n2 = n2->nxt)
{ for (n1 = q1->Other; n1; n1 = n1->nxt)
if (isequal(n1, n2))
break;
if (!n1)
{ Node *n3 = dupnode(n2);
/* don't mess up n2->nxt */
n3->nxt = q1->Other;
q1->Other = n3;
} }
map = (Mapping *) tl_emalloc(sizeof(Mapping));
map->from = g->name->name;
map->to = q1;
map->nxt = Mapped;
Mapped = map;
for (n1 = g->Other; n1; n1 = n2)
{ n2 = n1->nxt;
releasenode(1, n1);
}
for (n1 = g->Old; n1; n1 = n2)
{ n2 = n1->nxt;
releasenode(1, n1);
}
for (n1 = g->Next; n1; n1 = n2)
{ n2 = n1->nxt;
releasenode(1, n1);
}
return 1;
}
if (newstates) tl_verbose=1;
Debug2(" New Node %s [", g->name->name);
for (n1 = g->Old; n1; n1 = n1->nxt)
{ Dump(n1); Debug(", "); }
Debug2("] nr %d\n", Base);
if (newstates) tl_verbose=0;
Base++;
g->nxt = Nodes_Set;
Nodes_Set = g;
return 0;
}
static void
expand_g(Graph *g)
{ Node *now, *n1, *n2, *nx;
int can_release;
if (!g->New)
{ Debug2("\nDone with %s", g->name->name);
if (tl_verbose) dump_graph(g);
if (not_new(g))
{ if (tl_verbose) printf("\tIs Not New\n");
return;
}
if (g->Next)
{ Debug(" Has Next [");
for (n1 = g->Next; n1; n1 = n1->nxt)
{ Dump(n1); Debug(", "); }
Debug("]\n");
ng(ZS, getsym(g->name), g->Next, ZN, ZN);
}
return;
}
if (tl_verbose)
{ Symbol *z;
printf("\nExpand %s, from ", g->name->name);
for (z = g->incoming; z; z = z->next)
printf("%s, ", z->name);
printf("\n\thandle:\t"); Explain(g->New->ntyp);
dump_graph(g);
}
if (g->New->ntyp == AND)
{ if (g->New->nxt)
{ n2 = g->New->rgt;
while (n2->nxt)
n2 = n2->nxt;
n2->nxt = g->New->nxt;
}
n1 = n2 = g->New->lft;
while (n2->nxt)
n2 = n2->nxt;
n2->nxt = g->New->rgt;
releasenode(0, g->New);
g->New = n1;
push_stack(g);
return;
}
can_release = 0; /* unless it need not go into Old */
now = g->New;
g->New = g->New->nxt;
now->nxt = ZN;
if (now->ntyp != TRUE)
{ if (g->Old)
{ for (n1 = g->Old; n1->nxt; n1 = n1->nxt)
if (isequal(now, n1))
{ can_release = 1;
goto out;
}
n1->nxt = now;
} else
g->Old = now;
}
out:
switch (now->ntyp) {
case FALSE:
push_stack(g);
break;
case TRUE:
releasenode(1, now);
push_stack(g);
break;
case PREDICATE:
case NOT:
if (can_release) releasenode(1, now);
push_stack(g);
break;
case V_OPER:
Assert(now->rgt != ZN, now->ntyp);
Assert(now->lft != ZN, now->ntyp);
Assert(now->rgt->nxt == ZN, now->ntyp);
Assert(now->lft->nxt == ZN, now->ntyp);
n1 = now->rgt;
n1->nxt = g->New;
if (can_release)
nx = now;
else
nx = getnode(now); /* now also appears in Old */
nx->nxt = g->Next;
n2 = now->lft;
n2->nxt = getnode(now->rgt);
n2->nxt->nxt = g->New;
g->New = flatten(n2);
push_stack(g);
ng(ZS, g->incoming, n1, g->Old, nx);
break;
case U_OPER:
Assert(now->rgt->nxt == ZN, now->ntyp);
Assert(now->lft->nxt == ZN, now->ntyp);
n1 = now->lft;
if (can_release)
nx = now;
else
nx = getnode(now); /* now also appears in Old */
nx->nxt = g->Next;
n2 = now->rgt;
n2->nxt = g->New;
goto common;
#ifdef NXT
case NEXT:
Assert(now->lft != ZN, now->ntyp);
nx = dupnode(now->lft);
nx->nxt = g->Next;
g->Next = nx;
if (can_release) releasenode(0, now);
push_stack(g);
break;
#endif
case OR:
Assert(now->rgt->nxt == ZN, now->ntyp);
Assert(now->lft->nxt == ZN, now->ntyp);
n1 = now->lft;
nx = g->Next;
n2 = now->rgt;
n2->nxt = g->New;
common:
n1->nxt = g->New;
ng(ZS, g->incoming, n1, g->Old, nx);
g->New = flatten(n2);
if (can_release) releasenode(1, now);
push_stack(g);
break;
}
}
Node *
twocases(Node *p)
{ Node *q;
/* 1: ([]p1 && []p2) == [](p1 && p2) */
/* 2: (<>p1 || <>p2) == <>(p1 || p2) */
if (!p) return p;
switch(p->ntyp) {
case AND:
case OR:
case U_OPER:
case V_OPER:
p->lft = twocases(p->lft);
p->rgt = twocases(p->rgt);
break;
#ifdef NXT
case NEXT:
#endif
case NOT:
p->lft = twocases(p->lft);
break;
default:
break;
}
if (p->ntyp == AND /* 1 */
&& p->lft->ntyp == V_OPER
&& p->lft->lft->ntyp == FALSE
&& p->rgt->ntyp == V_OPER
&& p->rgt->lft->ntyp == FALSE)
{ q = tl_nn(V_OPER, False,
tl_nn(AND, p->lft->rgt, p->rgt->rgt));
} else
if (p->ntyp == OR /* 2 */
&& p->lft->ntyp == U_OPER
&& p->lft->lft->ntyp == TRUE
&& p->rgt->ntyp == U_OPER
&& p->rgt->lft->ntyp == TRUE)
{ q = tl_nn(U_OPER, True,
tl_nn(OR, p->lft->rgt, p->rgt->rgt));
} else
q = p;
return q;
}
void
trans(Node *p)
{ Node *op;
Graph *g;
if (!p || tl_errs) return;
p = twocases(p);
if (tl_verbose || tl_terse)
{ fprintf(tl_out, "\t/* Normlzd: ");
dump(p);
fprintf(tl_out, " */\n");
}
if (tl_terse)
return;
op = dupnode(p);
ng(ZS, getsym(tl_lookup("init")), p, ZN, ZN);
while ((g = Nodes_Stack) != (Graph *) 0)
{ Nodes_Stack = g->nxt;
expand_g(g);
}
if (newstates)
return;
fixinit(p);
liveness(flatten(op)); /* was: liveness(op); */
mkbuchi();
if (tl_verbose)
{ printf("/*\n");
printf(" * %d states in Streett automaton\n", Base);
printf(" * %d Streett acceptance conditions\n", Max_Red);
printf(" * %d Buchi states\n", Total);
printf(" */\n");
}
}
|