/***** spin: flow.c *****/

/* Copyright (c) 1989-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            */

#include "spin.h"
#include "y.tab.h"

extern Symbol	*Fname;
extern int	nr_errs, lineno, verbose, in_for;
extern short	has_unless, has_badelse, has_xu;
extern char CurScope[MAXSCOPESZ];

Element *Al_El = ZE;
Label	*labtab = (Label *) 0;
int	Unique = 0, Elcnt = 0, DstepStart = -1;
int	initialization_ok = 1;

static Lbreak	*breakstack = (Lbreak *) 0;
static Lextok	*innermost;
static SeqList	*cur_s = (SeqList *) 0;
static int	break_id=0;

static Element	*if_seq(Lextok *);
static Element	*new_el(Lextok *);
static Element	*unless_seq(Lextok *);
static void	add_el(Element *, Sequence *);
static void	attach_escape(Sequence *, Sequence *);
static void	mov_lab(Symbol *, Element *, Element *);
static void	walk_atomic(Element *, Element *, int);

void
open_seq(int top)
{	SeqList *t;
	Sequence *s = (Sequence *) emalloc(sizeof(Sequence));

	t = seqlist(s, cur_s);
	cur_s = t;
	if (top)
	{	Elcnt = 1;
		initialization_ok = 1;
	}
}

void
rem_Seq(void)
{
	DstepStart = Unique;
}

void
unrem_Seq(void)
{
	DstepStart = -1;
}

static int
Rjumpslocal(Element *q, Element *stop)
{	Element *lb, *f;
	SeqList *h;

	/* allow no jumps out of a d_step sequence */
	for (f = q; f && f != stop; f = f->nxt)
	{	if (f && f->n && f->n->ntyp == GOTO)
		{	lb = get_lab(f->n, 0);
			if (!lb || lb->Seqno < DstepStart)
			{	lineno = f->n->ln;
				Fname = f->n->fn;
				return 0;
		}	}
		for (h = f->sub; h; h = h->nxt)
		{	if (!Rjumpslocal(h->this->frst, h->this->last))
				return 0;
	
	}	}
	return 1;
}

void
cross_dsteps(Lextok *a, Lextok *b)
{
	if (a && b
	&&  a->indstep != b->indstep)
	{	lineno = a->ln;
		Fname  = a->fn;
		fatal("jump into d_step sequence", (char *) 0);
	}
}

int
is_skip(Lextok *n)
{
	return (n->ntyp == PRINT
	||	n->ntyp == PRINTM
	||	(n->ntyp == 'c'
		&& n->lft
		&& n->lft->ntyp == CONST
		&& n->lft->val  == 1));
}

void
check_sequence(Sequence *s)
{	Element *e, *le = ZE;
	Lextok *n;
	int cnt = 0;

	for (e = s->frst; e; le = e, e = e->nxt)
	{	n = e->n;
		if (is_skip(n) && !has_lab(e, 0))
		{	cnt++;
			if (cnt > 1
			&&  n->ntyp != PRINT
			&&  n->ntyp != PRINTM)
			{	if (verbose&32)
					printf("spin: %s:%d, redundant skip\n",
						n->fn->name, n->ln);
				if (e != s->frst
				&&  e != s->last
				&&  e != s->extent)
				{	e->status |= DONE;	/* not unreachable */
					le->nxt = e->nxt;	/* remove it */
					e = le;
				}
			}
		} else
			cnt = 0;
	}
}

void
prune_opts(Lextok *n)
{	SeqList *l;
	extern Symbol *context;
	extern char *claimproc;

	if (!n
	|| (context && claimproc && strcmp(context->name, claimproc) == 0))
		return;

	for (l = n->sl; l; l = l->nxt)	/* find sequences of unlabeled skips */
		check_sequence(l->this);
}

Sequence *
close_seq(int nottop)
{	Sequence *s = cur_s->this;
	Symbol *z;

	if (nottop == 0)	/* end of proctype body */
	{	initialization_ok = 1;
	}

	if (nottop > 0 && (z = has_lab(s->frst, 0)))
	{	printf("error: (%s:%d) label %s placed incorrectly\n",
			(s->frst->n)?s->frst->n->fn->name:"-",
			(s->frst->n)?s->frst->n->ln:0,
			z->name);
		switch (nottop) {
		case 1:
			printf("=====> stmnt unless Label: stmnt\n");
			printf("sorry, cannot jump to the guard of an\n");
			printf("escape (it is not a unique state)\n");
			break;
		case 2:
			printf("=====> instead of  ");
			printf("\"Label: stmnt unless stmnt\"\n");
			printf("=====> always use  ");
			printf("\"Label: { stmnt unless stmnt }\"\n");
			break;
		case 3:
			printf("=====> instead of  ");
			printf("\"atomic { Label: statement ... }\"\n");
			printf("=====> always use  ");
			printf("\"Label: atomic { statement ... }\"\n");
			break;
		case 4:
			printf("=====> instead of  ");
			printf("\"d_step { Label: statement ... }\"\n");
			printf("=====> always use  ");
			printf("\"Label: d_step { statement ... }\"\n");
			break;
		case 5:
			printf("=====> instead of  ");
			printf("\"{ Label: statement ... }\"\n");
			printf("=====> always use  ");
			printf("\"Label: { statement ... }\"\n");
			break;
		case 6:
			printf("=====>instead of\n");
			printf("	do (or if)\n");
			printf("	:: ...\n");
			printf("	:: Label: statement\n");
			printf("	od (of fi)\n");
			printf("=====>always use\n");
			printf("Label:	do (or if)\n");
			printf("	:: ...\n");
			printf("	:: statement\n");
			printf("	od (or fi)\n");
			break;
		case 7:
			printf("cannot happen - labels\n");
			break;
		}
		alldone(1);
	}

	if (nottop == 4
	&& !Rjumpslocal(s->frst, s->last))
		fatal("non_local jump in d_step sequence", (char *) 0);

	cur_s = cur_s->nxt;
	s->maxel = Elcnt;
	s->extent = s->last;
	if (!s->last)
		fatal("sequence must have at least one statement", (char *) 0);
	return s;
}

Lextok *
do_unless(Lextok *No, Lextok *Es)
{	SeqList *Sl;
	Lextok *Re = nn(ZN, UNLESS, ZN, ZN);
	Re->ln = No->ln;
	Re->fn = No->fn;

	has_unless++;
	if (Es->ntyp == NON_ATOMIC)
		Sl = Es->sl;
	else
	{	open_seq(0); add_seq(Es);
		Sl = seqlist(close_seq(1), 0);
	}

	if (No->ntyp == NON_ATOMIC)
	{	No->sl->nxt = Sl;
		Sl = No->sl;
	} else	if (No->ntyp == ':'
		&& (No->lft->ntyp == NON_ATOMIC
		||  No->lft->ntyp == ATOMIC
		||  No->lft->ntyp == D_STEP))
	{
		int tok = No->lft->ntyp;

		No->lft->sl->nxt = Sl;
		Re->sl = No->lft->sl;

		open_seq(0); add_seq(Re);
		Re = nn(ZN, tok, ZN, ZN);
		Re->sl = seqlist(close_seq(7), 0);
		Re->ln = No->ln;
		Re->fn = No->fn;

		Re = nn(No, ':', Re, ZN);	/* lift label */
		Re->ln = No->ln;
		Re->fn = No->fn;
		return Re;
	} else
	{	open_seq(0); add_seq(No);
		Sl = seqlist(close_seq(2), Sl);
	}

	Re->sl = Sl;
	return Re;
}

SeqList *
seqlist(Sequence *s, SeqList *r)
{	SeqList *t = (SeqList *) emalloc(sizeof(SeqList));

	t->this = s;
	t->nxt = r;
	return t;
}

static Element *
new_el(Lextok *n)
{	Element *m;

	if (n)
	{	if (n->ntyp == IF || n->ntyp == DO)
			return if_seq(n);
		if (n->ntyp == UNLESS)
			return unless_seq(n);
	}
	m = (Element *) emalloc(sizeof(Element));
	m->n = n;
	m->seqno = Elcnt++;
	m->Seqno = Unique++;
	m->Nxt = Al_El; Al_El = m;
	return m;
}

static int
has_chanref(Lextok *n)
{
	if (!n) return 0;

	switch (n->ntyp) {
	case 's':	case 'r':
#if 0
	case 'R':	case LEN:
#endif
	case FULL:	case NFULL:
	case EMPTY:	case NEMPTY:
		return 1;
	default:
		break;
	}
	if (has_chanref(n->lft))
		return 1;

	return has_chanref(n->rgt);
}

void
loose_ends(void)	/* properly tie-up ends of sub-sequences */
{	Element *e, *f;

	for (e = Al_El; e; e = e->Nxt)
	{	if (!e->n
		||  !e->nxt)
			continue;
		switch (e->n->ntyp) {
		case ATOMIC:
		case NON_ATOMIC:
		case D_STEP:
			f = e->nxt;
			while (f && f->n->ntyp == '.')
				f = f->nxt;
			if (0) printf("link %d, {%d .. %d} -> %d (ntyp=%d) was %d\n",
				e->seqno,
				e->n->sl->this->frst->seqno,
				e->n->sl->this->last->seqno,
				f?f->seqno:-1, f?f->n->ntyp:-1,
				e->n->sl->this->last->nxt?e->n->sl->this->last->nxt->seqno:-1);
			if (!e->n->sl->this->last->nxt)
				e->n->sl->this->last->nxt = f;
			else
			{	if (e->n->sl->this->last->nxt->n->ntyp != GOTO)
				{	if (!f || e->n->sl->this->last->nxt->seqno != f->seqno)
					non_fatal("unexpected: loose ends", (char *)0);
				} else
					e->n->sl->this->last = e->n->sl->this->last->nxt;
				/*
				 * fix_dest can push a goto into the nxt position
				 * in that case the goto wins and f is not needed
				 * but the last fields needs adjusting
				 */
			}
			break;
	}	}
}

static Element *
if_seq(Lextok *n)
{	int	tok = n->ntyp;
	SeqList	*s  = n->sl;
	Element	*e  = new_el(ZN);
	Element	*t  = new_el(nn(ZN,'.',ZN,ZN)); /* target */
	SeqList	*z, *prev_z = (SeqList *) 0;
	SeqList *move_else  = (SeqList *) 0;	/* to end of optionlist */
	int	ref_chans = 0;

	for (z = s; z; z = z->nxt)
	{	if (!z->this->frst)
			continue;
		if (z->this->frst->n->ntyp == ELSE)
		{	if (move_else)
				fatal("duplicate `else'", (char *) 0);
			if (z->nxt)	/* is not already at the end */
			{	move_else = z;
				if (prev_z)
					prev_z->nxt = z->nxt;
				else
					s = n->sl = z->nxt;
				continue;
			}
		} else
			ref_chans |= has_chanref(z->this->frst->n);
		prev_z = z;
	}
	if (move_else)
	{	move_else->nxt = (SeqList *) 0;
		/* if there is no prev, then else was at the end */
		if (!prev_z) fatal("cannot happen - if_seq", (char *) 0);
		prev_z->nxt = move_else;
		prev_z = move_else;
	}
	if (prev_z
	&&  ref_chans
	&&  prev_z->this->frst->n->ntyp == ELSE)
	{	prev_z->this->frst->n->val = 1;
		has_badelse++;
		if (has_xu)
		{	fatal("invalid use of 'else' combined with i/o and xr/xs assertions,",
				(char *)0);
		} else
		{	non_fatal("dubious use of 'else' combined with i/o,",
				(char *)0);
		}
		nr_errs--;
	}

	e->n = nn(n, tok, ZN, ZN);
	e->n->sl = s;			/* preserve as info only */
	e->sub = s;
	for (z = s; z; prev_z = z, z = z->nxt)
		add_el(t, z->this);	/* append target */
	if (tok == DO)
	{	add_el(t, cur_s->this); /* target upfront */
		t = new_el(nn(n, BREAK, ZN, ZN)); /* break target */
		set_lab(break_dest(), t);	/* new exit  */
		breakstack = breakstack->nxt;	/* pop stack */
	}
	add_el(e, cur_s->this);
	add_el(t, cur_s->this);
	return e;			/* destination node for label */
}

static void
escape_el(Element *f, Sequence *e)
{	SeqList *z;

	for (z = f->esc; z; z = z->nxt)
		if (z->this == e)
			return;	/* already there */

	/* cover the lower-level escapes of this state */
	for (z = f->esc; z; z = z->nxt)
		attach_escape(z->this, e);

	/* now attach escape to the state itself */

	f->esc = seqlist(e, f->esc);	/* in lifo order... */
#ifdef DEBUG
	printf("attach %d (", e->frst->Seqno);
	comment(stdout, e->frst->n, 0);
	printf(")	to %d (", f->Seqno);
	comment(stdout, f->n, 0);
	printf(")\n");
#endif
	switch (f->n->ntyp) {
	case UNLESS:
		attach_escape(f->sub->this, e);
		break;
	case IF:
	case DO:
		for (z = f->sub; z; z = z->nxt)
			attach_escape(z->this, e);
		break;
	case D_STEP:
		/* attach only to the guard stmnt */
		escape_el(f->n->sl->this->frst, e);
		break;
	case ATOMIC:
	case NON_ATOMIC:
		/* attach to all stmnts */
		attach_escape(f->n->sl->this, e);
		break;
	}
}

static void
attach_escape(Sequence *n, Sequence *e)
{	Element *f;

	for (f = n->frst; f; f = f->nxt)
	{	escape_el(f, e);
		if (f == n->extent)
			break;
	}
}

static Element *
unless_seq(Lextok *n)
{	SeqList	*s  = n->sl;
	Element	*e  = new_el(ZN);
	Element	*t  = new_el(nn(ZN,'.',ZN,ZN)); /* target */
	SeqList	*z;

	e->n = nn(n, UNLESS, ZN, ZN);
	e->n->sl = s;			/* info only */
	e->sub = s;

	/* need 2 sequences: normal execution and escape */
	if (!s || !s->nxt || s->nxt->nxt)
		fatal("unexpected unless structure", (char *)0);

	/* append the target state to both */
	for (z = s; z; z = z->nxt)
		add_el(t, z->this);

	/* attach escapes to all states in normal sequence */
	attach_escape(s->this, s->nxt->this);

	add_el(e, cur_s->this);
	add_el(t, cur_s->this);
#ifdef DEBUG
	printf("unless element (%d,%d):\n", e->Seqno, t->Seqno);
	for (z = s; z; z = z->nxt)
	{	Element *x; printf("\t%d,%d,%d :: ",
		z->this->frst->Seqno,
		z->this->extent->Seqno,
		z->this->last->Seqno);
		for (x = z->this->frst; x; x = x->nxt)
			printf("(%d)", x->Seqno);
		printf("\n");
	}
#endif
	return e;
}

Element *
mk_skip(void)
{	Lextok  *t = nn(ZN, CONST, ZN, ZN);
	t->val = 1;
	return new_el(nn(ZN, 'c', t, ZN));
}

static void
add_el(Element *e, Sequence *s)
{
	if (e->n->ntyp == GOTO)
	{	Symbol *z = has_lab(e, (1|2|4));
		if (z)
		{	Element *y; /* insert a skip */
			y = mk_skip();
			mov_lab(z, e, y); /* inherit label */
			add_el(y, s);
	}	}
#ifdef DEBUG
	printf("add_el %d after %d -- ",
	e->Seqno, (s->last)?s->last->Seqno:-1);
	comment(stdout, e->n, 0);
	printf("\n");
#endif
	if (!s->frst)
		s->frst = e;
	else
		s->last->nxt = e;
	s->last = e;
}

static Element *
colons(Lextok *n)
{
	if (!n)
		return ZE;
	if (n->ntyp == ':')
	{	Element *e = colons(n->lft);
		set_lab(n->sym, e);
		return e;
	}
	innermost = n;
	return new_el(n);
}

void
add_seq(Lextok *n)
{	Element *e;

	if (!n) return;
	innermost = n;
	e = colons(n);
	if (innermost->ntyp != IF
	&&  innermost->ntyp != DO
	&&  innermost->ntyp != UNLESS)
		add_el(e, cur_s->this);
}

void
show_lab(void)
{	Label *l;
	for (l = labtab; l; l = l->nxt)
		printf("label %s\n", l->s->name);
}

void
set_lab(Symbol *s, Element *e)
{	Label *l; extern Symbol *context;
	int cur_uiid = is_inline();

	if (!s) return;

	for (l = labtab; l; l = l->nxt)
	{	if (strcmp(l->s->name, s->name) == 0
		&&  l->c == context
		&&  l->uiid == cur_uiid)
		{	non_fatal("label %s redeclared", s->name);
			break;
	}	}

	l = (Label *) emalloc(sizeof(Label));
	l->s = s;
	l->c = context;
	l->e = e;
	l->uiid = cur_uiid;
	l->nxt = labtab;
	labtab = l;
}

static Label *
get_labspec(Lextok *n)
{	Symbol *s = n->sym;
	Label *l, *anymatch = (Label *) 0;
	int cur_uiid = n->uiid;
	/*
	 * try to find a label with the same uiid
	 * but if it doesn't exist, return any other
	 * that is defined within the same scope
	 */
	for (l = labtab; l; l = l->nxt)
	{	if (strcmp(s->name, l->s->name) == 0
		&&  s->context == l->s->context)
		{	anymatch = l;
			if (cur_uiid == l->uiid) /* best match */
			{	return l;
	}	}	}

	return anymatch; /* likely to be 0 */
}

Element *
get_lab(Lextok *n, int md)
{	Label *l = get_labspec(n);

	if (l != (Label *) 0)
	{	return (l->e);
	}

	if (md)
	{	lineno = n->ln;
		Fname  = n->fn;
		fatal("undefined label %s", n->sym->name);
	}

	return ZE;
}

Symbol *
has_lab(Element *e, int special)
{	Label *l;

	for (l = labtab; l; l = l->nxt)
	{	if (e != l->e)
			continue;
		if (special == 0
		||  ((special&1) && !strncmp(l->s->name, "accept", 6))
		||  ((special&2) && !strncmp(l->s->name, "end", 3))
		||  ((special&4) && !strncmp(l->s->name, "progress", 8)))
			return (l->s);
	}
	return ZS;
}

static void
mov_lab(Symbol *z, Element *e, Element *y)
{	Label *l;

	for (l = labtab; l; l = l->nxt)
		if (e == l->e)
		{	l->e = y;
			return;
		}
	if (e->n)
	{	lineno = e->n->ln;
		Fname  = e->n->fn;
	}
	fatal("cannot happen - mov_lab %s", z->name);
}

void
fix_dest(Symbol *c, Symbol *a)		/* c:label name, a:proctype name */
{	Label *l; extern Symbol *context;

#if 0
	printf("ref to label '%s' in proctype '%s', search:\n",
		c->name, a->name);
	for (l = labtab; l; l = l->nxt)
		printf("	%s in	%s\n", l->s->name, l->c->name);
#endif

	for (l = labtab; l; l = l->nxt)
	{	if (strcmp(c->name, l->s->name) == 0
		&&  strcmp(a->name, l->c->name) == 0)	/* ? */
			break;
	}
	if (!l)
	{	printf("spin: label '%s' (proctype %s)\n", c->name, a->name);
		non_fatal("unknown label '%s'", c->name);
		if (context == a)
		printf("spin: cannot remote ref a label inside the same proctype\n");
		return;
	}
	if (!l->e || !l->e->n)
		fatal("fix_dest error (%s)", c->name);
	if (l->e->n->ntyp == GOTO)
	{	Element	*y = (Element *) emalloc(sizeof(Element));
		int	keep_ln = l->e->n->ln;
		Symbol	*keep_fn = l->e->n->fn;

		/* insert skip - or target is optimized away */
		y->n = l->e->n;		  /* copy of the goto   */
		y->seqno = find_maxel(a); /* unique seqno within proc */
		y->nxt = l->e->nxt;
		y->Seqno = Unique++; y->Nxt = Al_El; Al_El = y;

		/* turn the original element+seqno into a skip */
		l->e->n = nn(ZN, 'c', nn(ZN, CONST, ZN, ZN), ZN);
		l->e->n->ln = l->e->n->lft->ln = keep_ln;
		l->e->n->fn = l->e->n->lft->fn = keep_fn;
		l->e->n->lft->val = 1;
		l->e->nxt = y;		/* append the goto  */
	}
	l->e->status |= CHECK2;	/* treat as if global */
	if (l->e->status & (ATOM | L_ATOM | D_ATOM))
	{	non_fatal("cannot reference label inside atomic or d_step (%s)",
			c->name);
	}
}

int
find_lab(Symbol *s, Symbol *c, int markit)
{	Label *l;

	for (l = labtab; l; l = l->nxt)
	{	if (strcmp(s->name, l->s->name) == 0
		&&  strcmp(c->name, l->c->name) == 0)
		{	l->visible |= markit;
			return (l->e->seqno);
	}	}
	return 0;
}

void
pushbreak(void)
{	Lbreak *r = (Lbreak *) emalloc(sizeof(Lbreak));
	Symbol *l;
	char buf[64];

	sprintf(buf, ":b%d", break_id++);
	l = lookup(buf);
	r->l = l;
	r->nxt = breakstack;
	breakstack = r;
}

Symbol *
break_dest(void)
{
	if (!breakstack)
		fatal("misplaced break statement", (char *)0);
	return breakstack->l;
}

void
make_atomic(Sequence *s, int added)
{	Element *f;

	walk_atomic(s->frst, s->last, added);

	f = s->last;
	switch (f->n->ntyp) {	/* is last step basic stmnt or sequence ? */
	case NON_ATOMIC:
	case ATOMIC:
		/* redo and search for the last step of that sequence */
		make_atomic(f->n->sl->this, added);
		break;

	case UNLESS:
		/* escapes are folded into main sequence */
		make_atomic(f->sub->this, added);
		break;

	default:
		f->status &= ~ATOM;
		f->status |= L_ATOM;
		break;
	}
}

#if 0
static int depth = 0;
void dump_sym(Symbol *, char *);

void
dump_lex(Lextok *t, char *s)
{	int i;

	depth++;
	printf(s);
	for (i = 0; i < depth; i++)
		printf("\t");
	explain(t->ntyp);
	if (t->ntyp == NAME) printf(" %s ", t->sym->name);
	if (t->ntyp == CONST) printf(" %d ", t->val);
	if (t->ntyp == STRUCT)
	{	dump_sym(t->sym, "\n:Z:");
	}
	if (t->lft)
	{	dump_lex(t->lft, "\nL");
	}
	if (t->rgt)
	{	dump_lex(t->rgt, "\nR");
	}	
	depth--;
}
void
dump_sym(Symbol *z, char *s)
{	int i;
	char txt[64];
	depth++;
	printf(s);
	for (i = 0; i < depth; i++)
		printf("\t");

	if (z->type == CHAN)
	{	if (z->ini && z->ini->rgt && z->ini->rgt->sym)
		{	// dump_sym(z->ini->rgt->sym, "\n:I:"); /* could also be longer list */
			if (z->ini->rgt->rgt
			|| !z->ini->rgt->sym)
			fatal("chan %s in for should have only one field (a typedef)", z->name);
			printf(" -- %s %p -- ", z->ini->rgt->sym->name, z->ini->rgt->sym);
		}
	} else if (z->type == STRUCT)
	{	if (z->Snm)
			printf(" == %s %p == ", z->Snm->name, z->Snm);
		else
		{	if (z->Slst)
				dump_lex(z->Slst, "\n:X:");
			if (z->ini)
				dump_lex(z->ini, "\n:I:");
		}
	}
	depth--;

}
#endif

int
match_struct(Symbol *s, Symbol *t)
{
	if (!t
	||  !t->ini
	||  !t->ini->rgt
	||  !t->ini->rgt->sym
	||   t->ini->rgt->rgt)
	{	fatal("chan %s in for should have only one field (a typedef)", t->name);
	}
	/* we already know that s is a STRUCT */
	if (0)
	{	printf("index type %s %p ==\n", s->Snm->name, s->Snm);
		printf("chan type  %s %p --\n\n", t->ini->rgt->sym->name, t->ini->rgt->sym);
	}

	return (s->Snm == t->ini->rgt->sym);
}

void
valid_name(Lextok *a3, Lextok *a5, Lextok *a8, char *tp)
{
	if (a3->ntyp != NAME)
	{	fatal("%s ( .name : from .. to ) { ... }", tp);
	}
	if (a3->sym->type == CHAN
	||  a3->sym->type == STRUCT
	||  a3->sym->isarray != 0)
	{	fatal("bad index in for-construct %s", a3->sym->name);
	}
	if (a5->ntyp == CONST && a8->ntyp == CONST && a5->val > a8->val)
	{	non_fatal("start value for %s exceeds end-value", a3->sym->name);
	}
}

void
for_setup(Lextok *a3, Lextok *a5, Lextok *a8)
{	/* for ( a3 : a5 .. a8 ) */

	valid_name(a3, a5, a8, "for");
	/* a5->ntyp = a8->ntyp = CONST; */
	add_seq(nn(a3, ASGN, a3, a5));	/* start value */
	open_seq(0);
	add_seq(nn(ZN, 'c', nn(a3, LE, a3, a8), ZN));	/* condition */
}

Lextok *
for_index(Lextok *a3, Lextok *a5)
{	Lextok *z0, *z1, *z2, *z3;
	Symbol *tmp_cnt;
	char tmp_nm[MAXSCOPESZ];
	/* for ( a3 in a5 ) { ... } */

	if (a3->ntyp != NAME)
	{	fatal("for ( .name in name ) { ... }", (char *) 0);
	}

	if (a5->ntyp != NAME)
	{	fatal("for ( %s in .name ) { ... }", a3->sym->name);
	}

	if (a3->sym->type == STRUCT)
	{	if (a5->sym->type != CHAN)
		{	fatal("for ( %s in .channel_name ) { ... }",
				a3->sym->name);
		}
		z0 = a5->sym->ini;
		if (!z0
		|| z0->val <= 0
		|| z0->rgt->ntyp != STRUCT
		|| z0->rgt->rgt != NULL)
		{	fatal("bad channel type %s in for", a5->sym->name);
		}

		if (!match_struct(a3->sym, a5->sym))
		{	fatal("type of %s does not match chan", a3->sym->name);
		}

		z1 = nn(ZN, CONST, ZN, ZN); z1->val = 0;
		z2 = nn(a5, LEN, a5, ZN);

		sprintf(tmp_nm, "_f0r_t3mp%s", CurScope); /* make sure it's unique */
		tmp_cnt = lookup(tmp_nm);
		if (z0->val > 255)			/* check nr of slots, i.e. max length */
		{	tmp_cnt->type = SHORT;	/* should be rare */
		} else
		{	tmp_cnt->type = BYTE;
		}
		z3 = nn(ZN, NAME, ZN, ZN);
		z3->sym = tmp_cnt;

		add_seq(nn(z3, ASGN, z3, z1));	/* start value 0 */

		open_seq(0);

		add_seq(nn(ZN, 'c', nn(z3, LT, z3, z2), ZN));	/* condition */

		/* retrieve  message from the right slot -- for now: rotate contents */
		in_for = 0;
		add_seq(nn(a5, 'r', a5, expand(a3, 1)));	/* receive */
		add_seq(nn(a5, 's', a5, expand(a3, 1)));	/* put back in to rotate */
		in_for = 1;
		return z3;
	} else
	{	if (a5->sym->isarray == 0
		||  a5->sym->nel <= 0)
		{	fatal("bad arrayname %s", a5->sym->name);
		}
		z1 = nn(ZN, CONST, ZN, ZN); z1->val = 0;
		z2 = nn(ZN, CONST, ZN, ZN); z2->val = a5->sym->nel - 1;
		for_setup(a3, z1, z2);
		return a3;
	}
}

Lextok *
for_body(Lextok *a3, int with_else)
{	Lextok *t1, *t2, *t0, *rv;

	rv = nn(ZN, CONST, ZN, ZN); rv->val = 1;
	rv = nn(ZN,  '+', a3, rv);
	rv = nn(a3, ASGN, a3, rv);
	add_seq(rv);	/* initial increment */

	pushbreak();

	/* completed loop body, main sequence */
	t1 = nn(ZN, 0, ZN, ZN);
	t1->sq = close_seq(8);

	open_seq(0);		/* add else -> break sequence */
	if (with_else)
	{	add_seq(nn(ZN, ELSE, ZN, ZN));
	}
	t2 = nn(ZN, GOTO, ZN, ZN);
	t2->sym = break_dest();
	add_seq(t2);
	t2 = nn(ZN, 0, ZN, ZN);
	t2->sq = close_seq(9);

	t0 = nn(ZN, 0, ZN, ZN);
	t0->sl = seqlist(t2->sq, seqlist(t1->sq, 0));

	rv = nn(ZN, DO, ZN, ZN);
	rv->sl = t0->sl;
	return rv;
}

Lextok *
sel_index(Lextok *a3, Lextok *a5, Lextok *a7)
{	/* select ( a3 : a5 .. a7 ) */

	valid_name(a3, a5, a7, "select");
	/* a5->ntyp = a7->ntyp = CONST; */

	add_seq(nn(a3, ASGN, a3, a5));	/* start value */
	open_seq(0);
	add_seq(nn(ZN, 'c', nn(a3, LT, a3, a7), ZN));	/* condition */

	return for_body(a3, 0);	/* no else, just a non-deterministic break */
}

static void
walk_atomic(Element *a, Element *b, int added)
{	Element *f; Symbol *ofn; int oln;
	SeqList *h;

	ofn = Fname;
	oln = lineno;
	for (f = a; ; f = f->nxt)
	{	f->status |= (ATOM|added);
		switch (f->n->ntyp) {
		case ATOMIC:
			if (verbose&32)
			  printf("spin: warning, %s:%d, atomic inside %s (ignored)\n",
			  f->n->fn->name, f->n->ln, (added)?"d_step":"atomic");
			goto mknonat;
		case D_STEP:
			if (!(verbose&32))
			{	if (added) goto mknonat;
				break;
			}
			printf("spin: warning, %s:%d, d_step inside ",
			 f->n->fn->name, f->n->ln);
			if (added)
			{	printf("d_step (ignored)\n");
				goto mknonat;
			}
			printf("atomic\n");
			break;
		case NON_ATOMIC:
mknonat:		f->n->ntyp = NON_ATOMIC; /* can jump here */
			h = f->n->sl;
			walk_atomic(h->this->frst, h->this->last, added);
			break;
		case UNLESS:
			if (added)
			{ printf("spin: error, %s:%d, unless in d_step (ignored)\n",
			 	 f->n->fn->name, f->n->ln);
			}
		}
		for (h = f->sub; h; h = h->nxt)
			walk_atomic(h->this->frst, h->this->last, added);
		if (f == b)
			break;
	}
	Fname = ofn;
	lineno = oln;
}

void
dumplabels(void)
{	Label *l;

	for (l = labtab; l; l = l->nxt)
		if (l->c != 0 && l->s->name[0] != ':')
		{	printf("label	%s	%d	",
				l->s->name, l->e->seqno);
			if (l->uiid == 0)
				printf("<%s>\n", l->c->name);
			else
				printf("<%s i%d>\n", l->c->name, l->uiid);
		}
}
