#include "common"

/*
	ref(p)->value = truth value of condition
	p->prev = top of former local region
	p->tbran, fbran -> created symbols for conditional jump targets
	p->start, sbias = elc at top of loop
*/

doarg(p1, p2) struct at *p1, *p2;
	{extern int stackoff;
	if (skip) return;
	if (atime)
		{if (test(p1, DEFF) ||
			test(p1->value = localize((p1->value)->name,
			p1->flags & ATTR, MEM, 0, p1->size), DEFF) ||
			(p1->value)->value)
			{error("illegal declaration %s", s(p1));
			p1->value = NULL; }
		else if (p2) (p1->value)->value = p2->value;
		 }
	else {
		if (p2 == NULL && lookup(NULL, ".temp")->value)
			{geninst(byte(p1, NULL) + 0010016, p1, NULL);
			setvb(p1, 0, 0); }
		else {
			geninst(byte(p1, NULL) + 0010046, p1, NULL);
			setvb(p1, p2 ? p2->value + 1 : 1, 0);
			stackoff =+ 2;
			}
		}
	 }

endcall(p1, p2) struct at *p1, *p2;
	{extern int stackoff;
	int v;
	struct at *p, *q, *r;
	if (skip) return;
	if (atime)
		{set(p1, DCLF, TRUE);
		if (p2 == NULL) p1->size = 0;
		else {
			r = ref(p1);
			setvb(p1, r->value, r->bias);
			v = p1->value;
			for (p = p2->value; p; p = q)
				{q = p->value; p->value = 0;
				define(p, p1->flags & ATTR | DCLF, p1->type,
					v, p1->bias);
				if (p1->type == REG || p1->type == IREG)
					v = (v + p->size / 2)
						% 8;
				else v =+ p->size; }
			p1->size = v - p1->value;
			p1->value = v;
			if (1 <= p1->bias && p1->bias <= 3)
				{v = elc;
				setelc(p1->value, p1->bias);
				setelc(v->value, v->bias); }
			}
		 }
	else {
		endlocal(p1->prev);
		if (p1->type == LABEL) p1->type = p1->bias;
		geninst(004700, NULL, p1);		/* jsr pc,~ */
		if (p2) {switch (p2->value) {
	 case 0:	break;
	 case 1:	gen(005726, 0); break;		/* tst (sp)+ */
	 case 2:	gen(022626, 0); break;		/* cmp (sp)+,(sp)+ */
	 default:	gen(062706, 0);			/* add ~,sp */
			gen(2 * p2->value, 0);
						}
			stackoff =- 2 * p2->value;
			}
		ref(p1)->size = 0;
		p1->size = 2;
		set(p1, PUBF | BCCF, TRUE);
		p1->type = REG;
		setvb(p1, 0, 0);
		 }
	 }

dodecl(p1, p2) struct at *p1, *p2;
	{struct at *p, *q;
	if (skip) return;
	for (p = p2->value; p; p = q)
		{q = p->value; p->value = 0;
		if (p1->type == LOCAL)
			localize(p->name, p1->flags & ATTR | DCLF,
				MEM, 0, p1->size);
		else set(p, PUBF, TRUE);
		}
	}

dolabel(p1) struct at *p1;
	{if (skip) return;
	if (!atime)
		{p1->prev = spsave; spsave = sptop;
		if (!test(p1, DEFF))
			{p1->bias = p1->type;
			p1->type = LABEL; }
		}
	}

endlabel(p1) struct at *p1;
	{if (skip) return;
	endlocal(p1->prev);
	if (p1->type == LABEL)
		{define(p1->value, 0, MEM, p1->start, p1->sbias);
		(p1->value)->size = elc->value - p1->start; }
	else error("illegal label %s", s(p1));
	p1->type = ERR;
	}

endlocal(ps) struct symbol *ps;
	{struct symbol *p, *q;
	p = sptop; sptop = spsave;
	for ( ; p != spsave; p = q) {
		q = p->prev;
		if (!test(p, DEFF))
			{if (test(p, DCLF))
				error("undefined - %s", sn(p->name));
			else {p->prev = sptop; sptop = p; }
			 }
		if (debug > 1) error("%c%c %s %o+%o",
			test(p, DEFF) ? 'd' : ' ',
			test(p, DCLF) ? 'l' : ' ',
			sn(p->name), p->value, p->bias);
					}
	spsave = ps; }

begloop(p1, p2) struct at *p1, *p2;
	{if (skip) return;
	if (p1) p2->fbran = linkup(p2->fbran, p1->fbran);
	p2->type = DO;
	begthen(p2);
	}

begthen(p1) struct at *p1;
	{int k;
	if (skip) return;
	k = ref(p1)->value;
	if (k == FALSE && p1->tbran == NULL ||
		k == TRUE && p1->fbran == NULL)
		{endthen(p1);
		skip = !k;
		set(p1, SKPF, TRUE);
		setvb(p1, k, 0);
		if (debug && skip) error("skipping"); }
	else switch (next()) {
 case GOTO:
 case BREAK:	setvb(p1, FALSE, 0); setvb(&e, k, 0);
		e.tbran = p1->tbran; e.fbran = p1->fbran;
		p1->tbran = p1->fbran = NULL;
		break;

 case SCOLON:	break;

 default:	setvb(p1, TRUE, 0);
		p1->fbran = fjump(k ^ 1, p1->fbran, NULL);
		target(&(p1->tbran));
				}
	 }

endthen(p1) struct at *p1;
	{if (test(p1, SKPF))
		{skip = FALSE;
		if (debug) error("end skip"); }
	else {target(&(p1->tbran)); target(&(p1->fbran)); }
	 }

begelse(p1) struct at *p1;
	{int k;
	if (test(p1, SKPF))
		{set(p1, SKPF, skip = !skip);
		if (debug) error(skip ? "skipping" : "end skip"); }
	else	{k = ref(p1)->value;
		switch (next()) {
	 case GOTO:
	 case BREAK:	e.tbran = p1->fbran; e.fbran = p1->tbran;
			p1->tbran = p1->fbran = NULL;
			setvb(&e, k ^ 1, e.bias);
			break;

	 case SCOLON:	break;

	 default:	p1->tbran = fjump(k, p1->tbran, NULL);
			target(&(p1->fbran));
					}
		}
	 }

endloop(p1) struct at *p1;
	{if (skip) return;
	if (p1->tbran)
		{define(p1->tbran, 0, MEM, p1->start, p1->sbias);
		p1->tbran = NULL; }
	jump(ref(p1)->value, p1->start, p1->sbias);
	p1->type = ERR;
	}

begbreak(p1, p2) struct at *p1, *p2;
	{extern struct token stack[];
	struct token *p;
	int k;
	if (skip) return;
	k = ref(p1)->value;
	if (p2 == NULL || !test(p2, DEFF))
		{for (p = p1; p != stack; --p)
			if (p2 == NULL && (p->type == DO || p->type == LABEL)
				|| p2 && p->type == LABEL &&
				compare((p->value)->name, (p2->value)->name))
				{if (test(p1, BYTF))
					{if (p1->tbran) define(p1->tbran, 0,
						MEM, p->start, p->sbias);
					jump(k, p->start, p->sbias); }
				else p->fbran = fjump(k, p->fbran, p1->tbran);
				p1->tbran = NULL;
				endthen(p1);
				return; }
		}
	error("illegal break");
	}

begkbin(p1, p2) struct at *p1, *p2;
	{int k, op;
	if (skip) return;
	k = ref(p1)->value; op = ref(p2)->value;
	p1->type = ERR;
	if (atime) {
		if (k != TRUE && k != FALSE)
			error("illegal compile-time conditional %s %s",
				s(p1), s(p2));
		else if (k && op == OR || !k && op == AND)
			set(p1, SKPF, TRUE);
			}
	else {
		if (op == OR)
			{p1->tbran = fjump(k, p1->tbran, NULL);
			target(&(p1->fbran)); }
		else {
			p1->fbran = fjump(k ^ 1, p1->fbran, NULL);
			target(&(p1->tbran)); }
		}
	 }

endkbin(p1, p2) struct at *p1, *p2;
	{if (skip) return;
	if (!(atime && test(p1, SKPF))) setvb(p1, ref(p2)->value, 0);
	p1->tbran = linkup(p1->tbran, p2->tbran);
	p1->fbran = linkup(p1->fbran, p2->fbran);
	set(p1, SKPF, FALSE); }

dogoto(p1, p2) struct at *p1, *p2;
	{int k;
	struct at *p;
	if (skip) return;
	k = ref(p1)->value;
	p = ref(p2);
	if (test(p, DEFF) && p2->type == MEM) fjump(k, p, p1->tbran);
	else {
		k = (k ^ 1) << 8;
		switch (p2->type & ~R) {
	 case MIDX:
	 case IMIDX:
	 case CON:
	 case ICON:
	 case MEM:
	 case IMEM:	if (k) gen(k + 2, 0); break;	/* br over jmp */
	
	 default:	if (k) gen(k + 1, 0); break; }
		settoelc(p1->tbran);
		geninst(000100, NULL, p2);	/* jmp */
		}
	target(&(p1->fbran)); }
