/***** tl_spin: tl_main.c *****/

/*
* This file is part of the public release of Spin. It is subject to the
* terms in the LICENSE file that is included in this source directory.
* Tool documentation is available at http://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;

int     newstates  = 0; /* debugging only */
int     tl_errs    = 0;
int     tl_verbose = 0;
int     tl_terse   = 0;
int     tl_clutter = 0;
int     state_cnt = 0;

unsigned long   All_Mem = 0;
char    *claim_name;

static char     uform[4096];
static int      hasuform=0, cnt=0;

extern void cache_stats(void);
extern void a_stats(void);

int
tl_Getchar(void)
{
       if (cnt < hasuform)
               return uform[cnt++];
       cnt++;
       return -1;
}

int
tl_peek(int n)
{
       if (cnt+n < hasuform)
       {       return uform[cnt+n];
       }
       return -1;
}

void
tl_balanced(void)
{       int i;
       int k = 0;

       for (i = 0; i < hasuform; i++)
       {       if (uform[i] == '(')
               {       if (i > 0
                       && ((uform[i-1] == '"'  && uform[i+1] == '"')
                       ||  (uform[i-1] == '\'' && uform[i+1] == '\'')))
                       {       continue;
                       }
                       k++;
               } else if (uform[i] == ')')
               {       if (i > 0
                       && ((uform[i-1] == '"'  && uform[i+1] == '"')
                       ||  (uform[i-1] == '\'' && uform[i+1] == '\'')))
                       {       continue;
                       }
                       k--;
       }       }

       if (k != 0)
       {       tl_errs++;
               tl_yyerror("parentheses not balanced");
       }
}

void
put_uform(void)
{
       fprintf(tl_out, "%s", uform);
}

void
tl_UnGetchar(void)
{
       if (cnt > 0) cnt--;
}

static void
tl_stats(void)
{       extern int Stack_mx;
       printf("total memory used: %9ld\n", All_Mem);
       printf("largest stack sze: %9d\n", Stack_mx);
       cache_stats();
       a_stats();
}

int
tl_main(int argc, char *argv[])
{       int i;
       extern int xspin, s_trail;

       tl_verbose = 0; /* was: tl_verbose = verbose; */
       if (xspin && s_trail)
       {       tl_clutter = 1;
               /* generating claims for a replay should
                  be done the same as when generating the
                  pan.c that produced the error-trail */
       } else
       {       tl_clutter = 1-xspin;   /* use -X -f to turn off uncluttering */
       }
       newstates  = 0;
       state_cnt  = 0;
       tl_errs    = 0;
       tl_terse   = 0;
       All_Mem = 0;
       memset(uform, 0, sizeof(uform));
       hasuform=0;
       cnt=0;
       claim_name = (char *) 0;

       ini_buchi();
       ini_cache();
       ini_rewrt();
       ini_trans();

       while (argc > 1 && argv[1][0] == '-')
       {
               switch (argv[1][1]) {
               case 'd':       newstates = 1;  /* debugging mode */
                               break;
               case 'f':       argc--; argv++;
                               for (i = 0; argv[1][i]; i++)
                               {       if (argv[1][i] == '\t'
                                       ||  argv[1][i] == '\n')
                                               argv[1][i] = ' ';
                               }
                               strcpy(uform, argv[1]);
                               hasuform = (int) strlen(uform);
                               break;
               case 'v':       tl_verbose++;
                               break;
               case 'n':       tl_terse = 1;
                               break;
               case 'c':       argc--; argv++;
                               claim_name = (char *) emalloc(strlen(argv[1])+1);
                               strcpy(claim_name, argv[1]);
                               break;
               default :       printf("spin -f: saw '-%c'\n", argv[1][1]);
                               goto nogood;
               }
               argc--; argv++;
       }
       if (hasuform == 0)
       {
nogood:         printf("usage:\tspin [-v] [-n] -f formula\n");
               printf("        -v verbose translation\n");
               printf("        -n normalize tl formula and exit\n");
               exit(1);
       }
       tl_balanced();

       if (tl_errs == 0)
               tl_parse();

       if (tl_verbose) tl_stats();
       return tl_errs;
}

#define Binop(a)                \
               fprintf(tl_out, "(");   \
               dump(n->lft);           \
               fprintf(tl_out, a);     \
               dump(n->rgt);           \
               fprintf(tl_out, ")")

void
dump(Node *n)
{
       if (!n) return;

       switch(n->ntyp) {
       case OR:        Binop(" || "); break;
       case AND:       Binop(" && "); break;
       case U_OPER:    Binop(" U ");  break;
       case V_OPER:    Binop(" V ");  break;
#ifdef NXT
       case NEXT:
               fprintf(tl_out, "X");
               fprintf(tl_out, " (");
               dump(n->lft);
               fprintf(tl_out, ")");
               break;
#endif
       case NOT:
               fprintf(tl_out, "!");
               fprintf(tl_out, " (");
               dump(n->lft);
               fprintf(tl_out, ")");
               break;
       case FALSE:
               fprintf(tl_out, "false");
               break;
       case TRUE:
               fprintf(tl_out, "true");
               break;
       case PREDICATE:
               fprintf(tl_out, "(%s)", n->sym->name);
               break;
       case CEXPR:
               fprintf(tl_out, "c_expr");
               fprintf(tl_out, " {");
               dump(n->lft);
               fprintf(tl_out, "}");
               break;
       case -1:
               fprintf(tl_out, " D ");
               break;
       default:
               printf("Unknown token: ");
               tl_explain(n->ntyp);
               break;
       }
}

void
tl_explain(int n)
{
       switch (n) {
       case ALWAYS:    printf("[]"); break;
       case EVENTUALLY: printf("<>"); break;
       case IMPLIES:   printf("->"); break;
       case EQUIV:     printf("<->"); break;
       case PREDICATE: printf("predicate"); break;
       case OR:        printf("||"); break;
       case AND:       printf("&&"); break;
       case NOT:       printf("!"); break;
       case U_OPER:    printf("U"); break;
       case V_OPER:    printf("V"); break;
#ifdef NXT
       case NEXT:      printf("X"); break;
#endif
       case CEXPR:     printf("c_expr"); break;
       case TRUE:      printf("true"); break;
       case FALSE:     printf("false"); break;
       case ';':       printf("end of formula"); break;
       default:        printf("%c", n); break;
       }
}

static void
tl_non_fatal(char *s1, char *s2)
{       extern int tl_yychar;
       int i;

       printf("tl_spin: ");
#if 1
       printf(s1, s2); /* prevent a compiler warning */
#else
       if (s2)
               printf(s1, s2);
       else
               printf(s1);
#endif
       if (tl_yychar != -1 && tl_yychar != 0)
       {       printf(", saw '");
               tl_explain(tl_yychar);
               printf("'");
       }
       printf("\ntl_spin: %s\n---------", uform);
       for (i = 0; i < cnt; i++)
               printf("-");
       printf("^\n");
       fflush(stdout);
       tl_errs++;
}

void
tl_yyerror(char *s1)
{
       Fatal(s1, (char *) 0);
}

void
Fatal(char *s1, char *s2)
{
       tl_non_fatal(s1, s2);
       /* tl_stats(); */
       exit(1);
}