Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/***** tl_spin: tl_buchi.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 int tl_verbose, tl_clutter, Total, Max_Red;
extern char *claim_name;

FILE    *tl_out;        /* if standalone: = stdout; */

typedef struct Transition {
        Symbol *name;
        Node *cond;
        int redundant, merged, marked;
        struct Transition *nxt;
} Transition;

typedef struct State {
        Symbol  *name;
        Transition *trans;
        Graph   *colors;
        unsigned char redundant;
        unsigned char accepting;
        unsigned char reachable;
        struct State *nxt;
} State;

static State *never = (State *) 0;
static int hitsall;

void
ini_buchi(void)
{
        never = (State *) 0;
        hitsall = 0;
}

static int
sametrans(Transition *s, Transition *t)
{
        if (strcmp(s->name->name, t->name->name) != 0)
                return 0;
        return isequal(s->cond, t->cond);
}

static Node *
Prune(Node *p)
{
        if (p)
        switch (p->ntyp) {
        case PREDICATE:
        case NOT:
        case FALSE:
        case TRUE:
#ifdef NXT
        case NEXT:
#endif
                return p;
        case OR:
                p->lft = Prune(p->lft);
                if (!p->lft)
                {       releasenode(1, p->rgt);
                        return ZN;
                }
                p->rgt = Prune(p->rgt);
                if (!p->rgt)
                {       releasenode(1, p->lft);
                        return ZN;
                }
                return p;
        case AND:
                p->lft = Prune(p->lft);
                if (!p->lft)
                        return Prune(p->rgt);
                p->rgt = Prune(p->rgt);
                if (!p->rgt)
                        return p->lft;
                return p;
        }
        releasenode(1, p);
        return ZN;
}

static State *
findstate(char *nm)
{       State *b;
        for (b = never; b; b = b->nxt)
                if (!strcmp(b->name->name, nm))
                        return b;
        if (strcmp(nm, "accept_all"))
        {       if (strncmp(nm, "accept", 6))
                {       int i; char altnm[64];
                        for (i = 0; i < 64; i++)
                                if (nm[i] == '_')
                                        break;
                        if (i >= 64)
                                Fatal("name too long %s", nm);
                        sprintf(altnm, "accept%s", &nm[i]);
                        return findstate(altnm);
                }
        /*      Fatal("buchi: no state %s", nm); */
        }
        return (State *) 0;
}

static void
Dfs(State *b)
{       Transition *t;

        if (!b || b->reachable) return;
        b->reachable = 1;

        if (b->redundant)
                printf("/* redundant state %s */\n",
                        b->name->name);
        for (t = b->trans; t; t = t->nxt)
        {       if (!t->redundant)
                {       Dfs(findstate(t->name->name));
                        if (!hitsall
                        &&  strcmp(t->name->name, "accept_all") == 0)
                                hitsall = 1;
                }
        }
}

void
retarget(char *from, char *to)
{       State *b;
        Transition *t;
        Symbol *To = tl_lookup(to);

        if (tl_verbose) printf("replace %s with %s\n", from, to);

        for (b = never; b; b = b->nxt)
        {       if (!strcmp(b->name->name, from))
                        b->redundant = 1;
                else
                for (t = b->trans; t; t = t->nxt)
                {       if (!strcmp(t->name->name, from))
                                t->name = To;
        }       }
}

#ifdef NXT
static Node *
nonxt(Node *n)
{
        switch (n->ntyp) {
        case U_OPER:
        case V_OPER:
        case NEXT:
                return ZN;
        case OR:
                n->lft = nonxt(n->lft);
                n->rgt = nonxt(n->rgt);
                if (!n->lft || !n->rgt)
                        return True;
                return n;
        case AND:
                n->lft = nonxt(n->lft);
                n->rgt = nonxt(n->rgt);
                if (!n->lft)
                {       if (!n->rgt)
                                n = ZN;
                        else
                                n = n->rgt;
                } else if (!n->rgt)
                        n = n->lft;
                return n;
        }
        return n;
}
#endif

static Node *
combination(Node *s, Node *t)
{       Node *nc;
#ifdef NXT
        Node *a = nonxt(s);
        Node *b = nonxt(t);

        if (tl_verbose)
        {       printf("\tnonxtA: "); dump(a);
                printf("\n\tnonxtB: "); dump(b);
                printf("\n");
        }
        /* if there's only a X(f), its equivalent to true */
        if (!a || !b)
                nc = True;
        else
                nc = tl_nn(OR, a, b);
#else
        nc = tl_nn(OR, s, t);
#endif
        if (tl_verbose)
        {       printf("\tcombo: "); dump(nc);
                printf("\n");
        }
        return nc;
}

Node *
unclutter(Node *n, char *snm)
{       Node *t, *s, *v, *u;
        Symbol *w;

        /* check only simple cases like !q && q */
        for (t = n; t; t = t->rgt)
        {       if (t->rgt)
                {       if (t->ntyp != AND || !t->lft)
                                return n;
                        if (t->lft->ntyp != PREDICATE
#ifdef NXT
                        &&  t->lft->ntyp != NEXT
#endif
                        &&  t->lft->ntyp != NOT)
                                return n;
                } else
                {       if (t->ntyp != PREDICATE
#ifdef NXT
                        &&  t->ntyp != NEXT
#endif
                        &&  t->ntyp != NOT)
                                return n;
                }
        }

        for (t = n; t; t = t->rgt)
        {       if (t->rgt)
                        v = t->lft;
                else
                        v = t;
                if (v->ntyp == NOT
                &&  v->lft->ntyp == PREDICATE)
                {       w = v->lft->sym;
                        for (s = n; s; s = s->rgt)
                        {       if (s == t) continue;
                                if (s->rgt)
                                        u = s->lft;
                                else
                                        u = s;
                                if (u->ntyp == PREDICATE
                                &&  strcmp(u->sym->name, w->name) == 0)
                                {       if (tl_verbose)
                                        {       printf("BINGO %s:\t", snm);
                                                dump(n);
                                                printf("\n");
                                        }
                                        return False;
                                }
                        }
        }       }
        return n;
}

static void
clutter(void)
{       State *p;
        Transition *s;

        for (p = never; p; p = p->nxt)
        for (s = p->trans; s; s = s->nxt)
        {       s->cond = unclutter(s->cond, p->name->name);
                if (s->cond
                &&  s->cond->ntyp == FALSE)
                {       if (s != p->trans 
                        ||  s->nxt)
                                s->redundant = 1;
                }
        }
}

static void
showtrans(State *a)
{       Transition *s;

        for (s = a->trans; s; s = s->nxt)
        {       printf("%s ", s->name?s->name->name:"-");
                dump(s->cond);
                printf(" %d %d %d\n", s->redundant, s->merged, s->marked);
        }
}

static int
mergetrans(void)
{       State *b;
        Transition *s, *t;
        Node *nc; int cnt = 0;

        for (b = never; b; b = b->nxt)
        {       if (!b->reachable) continue;

                for (s = b->trans; s; s = s->nxt)
                {       if (s->redundant) continue;

                        for (t = s->nxt; t; t = t->nxt)
                        if (!t->redundant
                        &&  !strcmp(s->name->name, t->name->name))
                        {       if (tl_verbose)
                                {       printf("===\nstate %s, trans to %s redundant\n",
                                        b->name->name, s->name->name);
                                        showtrans(b);
                                        printf(" conditions ");
                                        dump(s->cond); printf(" <-> ");
                                        dump(t->cond); printf("\n");
                                }

                                if (!s->cond) /* same as T */
                                {       releasenode(1, t->cond); /* T or t */
                                        nc = True;
                                } else if (!t->cond)
                                {       releasenode(1, s->cond);
                                        nc = True;
                                } else
                                {       nc = combination(s->cond, t->cond);
                                }
                                t->cond = rewrite(nc);
                                t->merged = 1;
                                s->redundant = 1;
                                cnt++;
                                break;
        }       }       }
        return cnt;
}

static int
all_trans_match(State *a, State *b)
{       Transition *s, *t;
        int found, result = 0;

        if (a->accepting != b->accepting)
                goto done;

        for (s = a->trans; s; s = s->nxt)
        {       if (s->redundant) continue;
                found = 0;
                for (t = b->trans; t; t = t->nxt)
                {       if (t->redundant) continue;
                        if (sametrans(s, t))
                        {       found = 1;
                                t->marked = 1;
                                break;
                }       }
                if (!found)
                        goto done;
        }
        for (s = b->trans; s; s = s->nxt)
        {       if (s->redundant || s->marked) continue;
                found = 0;
                for (t = a->trans; t; t = t->nxt)
                {       if (t->redundant) continue;
                        if (sametrans(s, t))
                        {       found = 1;
                                break;
                }       }
                if (!found)
                        goto done;
        }
        result = 1;
done:
        for (s = b->trans; s; s = s->nxt)
                s->marked = 0;
        return result;
}

#ifndef NO_OPT
#define BUCKY
#endif

#ifdef BUCKY
static int
all_bucky(State *a, State *b)
{       Transition *s, *t;
        int found, result = 0;

        for (s = a->trans; s; s = s->nxt)
        {       if (s->redundant) continue;
                found = 0;
                for (t = b->trans; t; t = t->nxt)
                {       if (t->redundant) continue;

                        if (isequal(s->cond, t->cond))
                        {       if (strcmp(s->name->name, b->name->name) == 0
                                &&  strcmp(t->name->name, a->name->name) == 0)
                                {       found = 1;      /* they point to each other */
                                        t->marked = 1;
                                        break;
                                }
                                if (strcmp(s->name->name, t->name->name) == 0
                                &&  strcmp(s->name->name, "accept_all") == 0)
                                {       found = 1;
                                        t->marked = 1;
                                        break;
                                /* same exit from which there is no return */
                                }
                        }
                }
                if (!found)
                        goto done;
        }
        for (s = b->trans; s; s = s->nxt)
        {       if (s->redundant || s->marked) continue;
                found = 0;
                for (t = a->trans; t; t = t->nxt)
                {       if (t->redundant) continue;

                        if (isequal(s->cond, t->cond))
                        {       if (strcmp(s->name->name, a->name->name) == 0
                                &&  strcmp(t->name->name, b->name->name) == 0)
                                {       found = 1;
                                        t->marked = 1;
                                        break;
                                }
                                if (strcmp(s->name->name, t->name->name) == 0
                                &&  strcmp(s->name->name, "accept_all") == 0)
                                {       found = 1;
                                        t->marked = 1;
                                        break;
                                }
                        }
                }
                if (!found)
                        goto done;
        }
        result = 1;
done:
        for (s = b->trans; s; s = s->nxt)
                s->marked = 0;
        return result;
}

static int
buckyballs(void)
{       State *a, *b, *c, *d;
        int m, cnt=0;

        do {
                m = 0; cnt++;
                for (a = never; a; a = a->nxt)
                {       if (!a->reachable) continue;

                        if (a->redundant) continue;

                        for (b = a->nxt; b; b = b->nxt)
                        {       if (!b->reachable) continue;

                                if (b->redundant) continue;

                                if (all_bucky(a, b))
                                {       m++;
                                        if (tl_verbose)
                                        {       printf("%s bucky match %s\n",
                                                a->name->name, b->name->name);
                                        }

                                        if (a->accepting && !b->accepting)
                                        {       if (strcmp(b->name->name, "T0_init") == 0)
                                                {       c = a; d = b;
                                                        b->accepting = 1;
                                                } else
                                                {       c = b; d = a;
                                                }
                                        } else
                                        {       c = a; d = b;
                                        }

                                        retarget(c->name->name, d->name->name);
                                        if (!strncmp(c->name->name, "accept", 6)
                                        &&  Max_Red == 0)
                                        {       char buf[64];
                                                sprintf(buf, "T0%s", &(c->name->name[6]));
                                                retarget(buf, d->name->name);
                                        }
                                        break;
                                }
                }       }
        } while (m && cnt < 10);
        return cnt-1;
}
#endif

static int
mergestates(int v)
{       State *a, *b;
        int m, cnt=0;

        if (tl_verbose)
                return 0;

        do {
                m = 0; cnt++;
                for (a = never; a; a = a->nxt)
                {       if (v && !a->reachable) continue;

                        if (a->redundant) continue; /* 3.3.10 */

                        for (b = a->nxt; b; b = b->nxt)
                        {       if (v && !b->reachable) continue;

                                if (b->redundant) continue; /* 3.3.10 */

                                if (all_trans_match(a, b))
                                {       m++;
                                        if (tl_verbose)
                                        {       printf("%d: state %s equals state %s\n",
                                                cnt, a->name->name, b->name->name);
                                                showtrans(a);
                                                printf("==\n");
                                                showtrans(b);
                                        }
                                        retarget(a->name->name, b->name->name);
                                        if (!strncmp(a->name->name, "accept", 6)
                                        &&  Max_Red == 0)
                                        {       char buf[64];
                                                sprintf(buf, "T0%s", &(a->name->name[6]));
                                                retarget(buf, b->name->name);
                                        }
                                        break;
                                }
#if 0
                                else if (tl_verbose)
                                {       printf("\n%d: state %s differs from state %s [%d,%d]\n",
                                                cnt, a->name->name, b->name->name,
                                                a->accepting, b->accepting);
                                        showtrans(a);
                                        printf("==\n");
                                        showtrans(b);
                                        printf("\n");
                                }
#endif
                }       }
        } while (m && cnt < 10);
        return cnt-1;
}

static int tcnt;

static void
rev_trans(Transition *t) /* print transitions  in reverse order... */
{
        if (!t) return;
        rev_trans(t->nxt);

        if (t->redundant && !tl_verbose) return;
        fprintf(tl_out, "\t:: (");
        if (dump_cond(t->cond, t->cond, 1))
                fprintf(tl_out, "1");
        fprintf(tl_out, ") -> goto %s\n", t->name->name);
        tcnt++;
}

static void
printstate(State *b)
{
        if (!b || (!tl_verbose && !b->reachable)) return;

        b->reachable = 0;       /* print only once */
        fprintf(tl_out, "%s:\n", b->name->name);

        if (tl_verbose)
        {       fprintf(tl_out, "       /* ");
                dump(b->colors->Other);
                fprintf(tl_out, " */\n");
        }

        if (strncmp(b->name->name, "accept", 6) == 0
        &&  Max_Red == 0)
                fprintf(tl_out, "T0%s:\n", &(b->name->name[6]));

        fprintf(tl_out, "\tif\n");
        tcnt = 0;
        rev_trans(b->trans);
        if (!tcnt) fprintf(tl_out, "\t:: false\n");
        fprintf(tl_out, "\tfi;\n");
        Total++;
}

void
addtrans(Graph *col, char *from, Node *op, char *to)
{       State *b;
        Transition *t;

        t = (Transition *) tl_emalloc(sizeof(Transition));
        t->name = tl_lookup(to);
        t->cond = Prune(dupnode(op));

        if (tl_verbose)
        {       printf("\n%s <<\t", from); dump(op);
                printf("\n\t"); dump(t->cond);
                printf(">> %s\n", t->name->name);
        }
        if (t->cond) t->cond = rewrite(t->cond);

        for (b = never; b; b = b->nxt)
                if (!strcmp(b->name->name, from))
                {       t->nxt = b->trans;
                        b->trans = t;
                        return;
                }
        b = (State *) tl_emalloc(sizeof(State));
        b->name   = tl_lookup(from);
        b->colors = col;
        b->trans  = t;
        if (!strncmp(from, "accept", 6))
                b->accepting = 1;
        b->nxt = never;
        never  = b;
}

static void
clr_reach(void)
{       State *p;
        for (p = never; p; p = p->nxt)
                p->reachable = 0;
        hitsall = 0;
}

void
fsm_print(void)
{       State *b; int cnt1, cnt2=0;
        extern void put_uform(void);

        if (tl_clutter) clutter();

        b = findstate("T0_init");
        if (b && (Max_Red == 0))
                b->accepting = 1;

        mergestates(0); 
        b = findstate("T0_init");

        fprintf(tl_out, "never %s {    /* ", claim_name?claim_name:"");
                put_uform();
        fprintf(tl_out, " */\n");

        do {
                clr_reach();
                Dfs(b);
                cnt1 = mergetrans();
                cnt2 = mergestates(1);
                if (tl_verbose)
                        printf("/* >>%d,%d<< */\n", cnt1, cnt2);
        } while (cnt2 > 0);

#ifdef BUCKY
        buckyballs();
        clr_reach();
        Dfs(b);
#endif
        if (b && b->accepting)
                fprintf(tl_out, "accept_init:\n");

        if (!b && !never)
        {       fprintf(tl_out, "       0 /* false */;\n");
        } else
        {       printstate(b);  /* init state must be first */
                for (b = never; b; b = b->nxt)
                        printstate(b);
        }
        if (hitsall)
        fprintf(tl_out, "accept_all:\n  skip\n");
        fprintf(tl_out, "}\n");
}