
/* $Id: saol_syntax.c,v 1.7 1997/11/10 21:34:41 eds Exp $ */
/* $Log: saol_syntax.c,v $
 * Revision 1.7  1997/11/10  21:34:41  eds
 * Fixed initialization bug in add_sym_table_name.
 *
 * Revision 1.7  1997/11/10  21:34:41  eds
 * Fixed initialization bug in add_sym_table_name.
 *
 * Revision 1.6  1997/11/05  20:10:21  eds
 * Added tablemaps.
 *
 * Revision 1.5  1997/11/05  15:03:00  eds
 * Added vector parameters/return values/operators.
 *
 * Revision 1.4  1997/10/03  15:12:37  eds
 * Added sfsynth statement.
 *
 * Revision 1.3  1997/09/30  20:34:47  eds
 * If symbol already present in symbol table, don't add it.
 * Especially pertains for the macro expansion.
 *
 * Revision 1.2  1997/09/17  14:11:41  eds
 * Added spatialize statement.
 * */
/*********************************************************************

This software module was originally developed by

Eric D. Scheirer (MIT Media Laboratory)

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard.  ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation.

This software module is hereby released into the public domain.

***********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "y.tab.h"
#include "saol.h"
#include "saol_co_imp.h"
#include <string.h>
#include <malloc.h>

orc_global *new_orc_global(void) {
  orc_global *g;

  PROT_MAL(g,orc_global,new_orc_global);
  g->max_actp_ct = 0;
  g->srate = 0;
  g->krate = 0;
  g->inchan = 0;
  g->outchan = 0;
  g->gvars = NULL;
  g->routes = NULL;
  g->sends = NULL;
  g->inmod = NULL;
  g->seqs = NULL;

  return(g);
}


void sort_global_decls(sa_decoder *sa, orc_global *og, global_block *gl) {
  /* sort the global_block parsing into the slots and
     lists for the orchestra global structure */

  while (gl && gl->d) {
    switch (gl->d->type) {
    case SEND:
      if (!og->sends)
	og->sends = new_send_list(gl->d->senddef);
      else
	add_send_list(og->sends,gl->d->senddef);
      break;
    case ROUTE:
      if (!og->routes)
	og->routes = new_route_list(gl->d->routedef);
      else
	add_route_list(og->routes,gl->d->routedef);
      break;
    case INPUTMOD:
      if (og->inmod)
	warn_line("Only one input modifier per orchestra; overwriting.\n",
		  gl->d->lineno);
      og->inmod = gl->d->inmoddef;
      break;
    case VARDECL:
      if (!og->gvars) 
	og->gvars = new_vardecl_list();
      add_vardecl_list(og->gvars,gl->d->vardecl);  
      break;
    case SEQUENCE:
      if (!og->seqs)
	og->seqs = new_sequence_list(gl->d->seqdef);
      else
	add_sequence_list(og->seqs,gl->d->seqdef);
      break;
    case KRATE:
      if (og->krate)
	warn_line("Overwriting krate value.",gl->d->lineno);
      og->krate = gl->d->rtparam;
      break;
    case SRATE:
      if (og->srate)
	warn_line("Overwriting srate value.",gl->d->lineno);
      og->srate = gl->d->rtparam;
      break;
    case INCHANNELS:
      if (og->inchan)
	warn_line("Overwriting inchannels value.",gl->d->lineno);
      og->inchan = gl->d->rtparam;
      break;
    case OUTCHANNELS:
      if (og->outchan)
	warn_line("Overwriting outchannels value.",gl->d->lineno);
      og->outchan = gl->d->rtparam;
      break;
    default:
      interror("Unknown global type in sort_global_decls()!\n");
    }
    gl = gl->next;
  }
	
  if (sa->verbose) print_global(sa);
  if (fabs(floor((double)sa->all->g->srate/sa->all->g->krate) * sa->all->g->krate - sa->all->g->srate)
      > 0.0001) {
    printf("Orchestra error: Control rate doesn't evenly divide sampling rate.\n");
    exit(1);
  }

}


void print_global(sa_decoder *sa) {
  vardecl_list *vdl;
  namelist *nl;
  route_list *rl;
  send_list *sl;
  sequence_list *seql;
  
  printf("\nGlobal orchestra information: \n");
  printf("        srate: %d\n",sa->all->g->srate);
  printf("        krate: %d\n",sa->all->g->krate);
  printf("   inchannels: %d\n",sa->all->g->inchan);
  printf("  outchannels: %d\n",sa->all->g->outchan);
  printf("         vars: ");
  for (vdl = sa->all->g->gvars;vdl;vdl=vdl->next)
    for (nl = vdl->v->nl;nl;nl=nl->next) 
      printf("%s %c",nl->n->name,(vdl->v->imported + vdl->v->exported) ? '+' : 0);
  printf("\n");
  printf("       routes: ");
  for (rl = sa->all->g->routes;rl;rl=rl->next)
    for (nl = rl->r->instrs;nl;nl=nl->next)
      printf("%s -> |%s| ",nl->n->name,rl->r->bus);
  printf("\n");
  printf("        sends: ");
  for (sl = sa->all->g->sends;sl;sl=sl->next)
    printf("%s ",sl->s->instr);
  printf("\n");
  printf("       busses: ");
  for (sl = sa->all->g->sends;sl;sl=sl->next)
    for (nl = sl->s->busses;nl;nl=nl->next)
      printf("%s ",nl->n->name);
  printf("\n");
  printf("         seqs: ");
  for (seql = sa->all->g->seqs;seql;seql=seql->next) {
    for (nl = seql->s->seqlist;nl;nl=nl->next) {
      printf("%s",nl->n->name);
      if (nl->next)
	printf(" < ");
    }
    if (seql->next)
      printf("; ");
  }
  printf("\n");
  printf("     inputmod: %s\n",sa->all->g->inmod ? sa->all->g->inmod->instr : "none");
  printf("\n");
}


void build_sym_table(sa_decoder *sa) {
  /* build and check the symbol and bus tables for each instr, opcode, and the
     global block */
  instr_list *il;
  route_list *rl;
  send_list *sl;
  sequence_list *seql;
  opcode_list *op;
  char s[340];

  /* clean up input/output values */

  if (!sa->all->g->inchan)
    sa->all->g->inchan = sa->inchan;
  if (sa->all->g->inchan > sa->inchan) {
    sprintf(s,"Orchestra wants %d input channel(s); only %d given.  Rest will be zero.",sa->all->g->inchan, sa->inchan);
    warn_line(s,0);
  }
  if (sa->all->g->inchan < sa->inchan) {
    sprintf(s,"%d channels of input given; orchestra only wants %d.\n",
	    sa->inchan,sa->all->g->inchan);
    strcat(s,"         Ignoring extra channel(s).");
    warn_line(s,0);
  }
  if (!sa->all->g->outchan) {
    if (sa->all->g->inchan) {
      sa->all->g->outchan = sa->all->g->inchan;
      sprintf(s,"No outchannels given; defaulting to %d from input.",
	      sa->all->g->inchan);
    }
    else {
      sa->all->g->outchan = 1;
      sprintf(s,"No outchannels given; defaulting to 1.\n");
    }
    warn_line(s,0);
  }  

  /* add symbols: global block first */

  sa->all->g->sym = new_symtable(sa,0);
  add_sym_table_core_opcodes(sa,sa->all->g->sym);
  add_sym_table_decls(sa,sa->all->g->sym,sa->all->g->gvars);
  sa->all->g->bus = make_bus_table(sa,sa->all->g->sends);

  /* now each instr */
  for (il=sa->all->il;il;il=il->next) {
    il->i->sym = new_symtable(sa,1);
    add_sym_table_name(sa->all->g->sym,il->i->name,INSTR,il->i);
    add_sym_table_namelist(sa,il->i->sym,il->i->params,IVAR,NOTAG,FORMALPARAM);
    add_sym_table_decls(sa,il->i->sym,il->i->localvars);
  }

  /* and now each opcode */
  for (op=sa->all->op;op;op=op->next) {
    op->o->sym = new_symtable(sa,1);
    add_sym_table_name(sa->all->g->sym,op->o->name,op->o->type,op->o);
    add_sym_table_fplist(op->o->sym,op->o->params);
    add_sym_table_decls(sa,op->o->sym,op->o->localvars);
  }


  /* check symbols: sends in global */
  for (sl=sa->all->g->sends;sl;sl=sl->next) {
    check_sym_ref_exprlist(sa,sa->all->g->sym,sl->s->pfields);
    check_instr_ref_name(sa->all->g->sym,sl->s->instr);
  }

  /* routes in global */
  for (rl = sa->all->g->routes;rl;rl=rl->next) {
    check_bus(sa->all->g->bus,rl->r->bus);
    check_instr_ref_namelist(sa->all->g->sym,rl->r->instrs);
    attach_route(sa,sa->all->g->sym,rl->r->bus,rl->r->instrs);
  }
  
  /* sequences in global */
  for (seql=sa->all->g->seqs;seql;seql=seql->next) 
    check_instr_ref_namelist(sa->all->g->sym,seql->s->seqlist);

  /* parameters to global input modifier */
  if (sa->all->g->inmod) {
    check_instr_ref_name(sa->all->g->sym,sa->all->g->inmod->instr);
    check_sym_ref_exprlist(sa,sa->all->g->sym,sa->all->g->inmod->params);
  }

  /* now each instrument's code */
  for (il=sa->all->il;il;il=il->next) {
    set_instr_inchan(sa,il->i);
    check_sym_ref_vardecls(sa,il->i->sym,il->i->localvars);
    check_sym_ref_block(sa,il->i->sym,il->i->code);
    il->i->sym = remove_unused_symbols(sa,NULL,il->i->sym);
    /*    il->i->localspace = get_instr_localspace(sa,il->i); */
  }

  /* and each opcode's code */
  for (op=sa->all->op;op;op=op->next) {
    check_sym_ref_vardecls(sa,op->o->sym,op->o->localvars);
    check_sym_ref_block(sa,op->o->sym,op->o->code);
    op->o->sym = remove_unused_symbols(sa,NULL,op->o->sym);
  }
  sa->all->g->sym = remove_unused_symbols(sa,NULL,sa->all->g->sym);     

}

struct std_name_struct std_names[] = { { "input", 0, ASIG},  /* this has to go first for frame sizes to work */
				       { "s_rate", 1, IVAR },
				       { "k_rate", 1, IVAR },
				       { "inchan", 1, IVAR },
				       { "outchan", 1, IVAR },
				       { "time", 1, IVAR },
				       { "dur", 1, IVAR },
				       { "MIDIctrl", 128, KSIG},
				       { "MIDIbend", 1, KSIG},
				       { "MIDItouch", 1, KSIG},
				       { "released", 1, KSIG},
				       { "stolen", 1, KSIG},
				       { "cpuload", 1, KSIG} };

symtable *new_symtable(sa_decoder *sa,int use_std_names) {
  int i;
  symtable *s;
  symbol *sym;

  PROT_MAL(s,symtable,new_symtable);
  s->s = NULL;
  s->next = NULL;

  if (use_std_names)
    for (i=0;i!=13;i++) {
      sym = add_sym_table_name(s,std_names[i].name,std_names[i].type, NULL);
      sym->binding = STANDARD_NAME;
      sym->width = std_names[i].width;
    }
  return(s);
}

symbol *add_sym_table_name(symtable *t, char *name, long type, void *dref) {
  symbol *s;
  symtable *newt,*last,*t2;

  for (t2 = t;t2 && t2->s;t2=t2->next)
    if (!strcmp(t2->s->name,name))
      return t2->s;
  
  PROT_MAL(s,symbol,add_sym_table_name);
  s->name = strdup(name);
  s->width = 0;
  s->type = type;
  s->imported = 0;
  s->exported = 0;
  s->symtable_ref = t;
  s->defn = dref;
  s->glink = NULL;
  s->table = NULL;
  s->binding = 0;
  s->mark = 0;
  s->tablemap = NULL;

  if (t->s) {
    PROT_MAL(newt,symtable,add_sym_table_name);
    for (;t;last=t,t=t->next) ;
    newt->next = NULL;
    newt->s = s;
    last->next = newt;
  }
  else t->s = s;
  return s;
}

bustable *make_bus_table(sa_decoder *sa, send_list *sl) {
  bustable *bt,*first=NULL,*last;
  namelist *nl;

  if (!sl || !sl->s) return NULL;

  for (;sl;sl=sl->next) {
    for (nl=sl->s->busses;nl;nl=nl->next) {
      if (!get_bus(first,nl->n->name)) {
	PROT_MAL(bt,bustable,make_bus_table);
	bt->name = strdup(nl->n->name);
	if (!strcmp(bt->name,"input"))
	  sa->inbus = bt;
	bt->width = 0;  /* need to do later, when we check outputs */
	bt->next = NULL;
	bt->send = sl->s;
	if (!first) { first = bt; last = first; }
	else { last->next = bt; last = bt; }
      }
    }
  }
  return first;
}

void set_bus_width(bustable *bt, int width) {
  bt->width = width;
}

void set_instr_inchan(sa_decoder *sa, instr_decl *id) {
  bustable *bt;
  symbol *sym;

  for (bt=sa->all->g->bus;bt && strcmp(bt->send->instr,id->name);bt=bt->next);
  if (!bt)
    id->inchan = 0;
  else 
    id->inchan = bt->width;
    
  if (sym = get_sym_decl(id->sym,"input"))
    sym->width = id->inchan;
}
  
void add_sym_table_decls(sa_decoder *sa,symtable *t, vardecl_list *vdl) {
  symbol *newsym;
  char s[1000];
	
  for (; vdl; vdl =vdl->next) {
    if (vdl->v) {
      if (!vdl->v->tablemap_name)
	add_sym_table_namelist(sa,t,vdl->v->nl,vdl->v->type,
			       MAKE_IMPEXP_TAG(vdl->v->imported,vdl->v->exported),
			       LOCAL);
      if (vdl->v->t) { /* table definition */
				
	if (get_sym_decl(t,vdl->v->t->name)) {
	  sprintf(s,"Repeat declaration of table %s.",vdl->v->t->name);
	  parse_error(s);
	}
	newsym = add_sym_table_name(t,vdl->v->t->name,TABLE,vdl->v);
	newsym->binding = LOCAL;
	newsym->table = vdl->v->t;
	newsym->width = 1;
      }
      if (vdl->v->tablemap_name) { /* tablemap definition */
	newsym = add_sym_table_name(t,vdl->v->tablemap_name,TABLEMAP,vdl->v);
	newsym->tablemap = vdl->v->nl;
      }
    } 
  }
}

void add_sym_table_namelist(sa_decoder *sa,symtable *t, namelist *nl, long type, long gtag,
			    int binding) {
  char s[1000];
  symbol *newsym,*sym;

  for ( ; nl && nl->n ; nl=nl->next) { /* for each name declared in this defn */
  
    if (get_sym_decl(t,nl->n->name)) {
      sprintf(s,"Repeat declaration of '%s'",nl->n->name);
      parse_error_line(s,nl->lineno);
    }
      
    newsym = add_sym_table_name(t,nl->n->name,type,nl);
    newsym->width = nl->n->width;
    newsym->imported = (gtag == IMPEXP || gtag == IMPORTS) ;
    newsym->exported = (gtag == IMPEXP || gtag == EXPORTS) ;
    newsym->glink = NULL;
    newsym->binding = binding;
      
    if (gtag != NOTAG) {      /* if we're importing/exporting, and */
      if (t != sa->all->g->sym) { /*    we're not in the global block, and */
	if (sym = get_sym_decl(sa->all->g->sym,nl->n->name)) { /* we find name match */
	    
	  if (sym->type != type) { 
	    sprintf(s,"Imports/exports definition of '%s' doesn't match global type.",nl->n->name);
	    parse_error_line(s,nl->lineno);
	  }
	  else newsym->glink = sym;
	  
	} else  /* no match*/

	  if (type == TABLE) { /* must share with global block */
	    sprintf(s,"No global declaration for table placeholder '%s'.",
		    nl->n->name);
	    parse_error_line(s,nl->lineno);
	  }

	  else if (type == IVAR) { /* must share with global block */
	    sprintf(s,"No global declaration for ivar '%s'.",
		    nl->n->name);
	    parse_error_line(s,nl->lineno);
	  }
	  
      } else { /* in global block */

	if (type == IVAR || type == TABLE) {
	  sprintf(s,"Only ksigs and asigs may be shared with host.");
	  parse_error_line(s,nl->lineno);
	}
      }
       
    } else { /* gtag == NOTAG; not import/export */
      if (type == TABLE) {
        if (t == sa->all->g->sym) { /* if we're in the global block */
	  sprintf(s,"Table placeholder '%s' not allowed in global block.",
		  nl->n->name);
	  parse_error_line(s,nl->lineno);
	}
	else {
	  sprintf(s,"Table placeholder '%s' must be global.",
		  nl->n->name);
	  parse_error_line(s,nl->lineno);
	}
      }
    }
  }
}

void add_sym_table_fplist(symtable *t, formalparam_list *fpl) {
  symbol *sym;
  
  if (fpl->fp)
    for (; fpl; fpl=fpl->next) {
      sym = add_sym_table_name(t,fpl->fp->n->name,fpl->fp->type,fpl);
      sym->binding = FORMALPARAM;
      sym->imported = 1;  /* because tables are always call-by-ref,
			     we need to treat them as imported for
			     releasing a context frame*/
      sym->width = fpl->fp->n->width;
      sym->mark = 1;
    }
}

void check_sym_ref_vardecls(sa_decoder *sa, symtable *t, vardecl_list *vdl) {
  symbol *sym;
  namelist *nl;
  char s[800];
  
  for (; vdl && vdl->v; vdl=vdl->next) {
    if (vdl->v->t) /* table */
      check_sym_ref_exprlist(sa,t,vdl->v->t->params);
    if (vdl->v->tablemap_name) { /* tablemap */
      for (nl=vdl->v->nl;nl && nl->n; nl=nl->next)
	if (!(sym = get_sym_decl(t,nl->n->name)) || (sym->type != TABLE)) {
	  sprintf(s,"'%s' is not a wavetable.",nl->n->name);
	  parse_error_line(s,vdl->v->lineno);
	}
	else
	  sym->mark = 1;
    }
  }
}
  
void check_sym_ref_exprlist(sa_decoder *sa,symtable *t, exprlist *el) {
  if (!el->p) return;
  for (;el;el = el->next)
    check_sym_ref_expr(sa,t,el->p);
}

void check_sym_ref_expr(sa_decoder *sa,symtable *t, expr *p) {
  char s[500];
  symbol *sym;
  opcode_decl *op;
  
  if (!p) return;
  switch (p->op) {
  case IDENT:
    if (!(sym = get_sym_decl(t,p->d->iname))) {
      sprintf(s,"Unknown symbol '%s'.",p->d->iname);
      parse_error_line(s,p->lineno);
    }
    else {
      sym->mark = 1;
      p->d->sym = sym;
      p->width = sym->width;
      if (sym->width == 0)
	parse_error_line("Can't use 'input' in instrument with no send.",p->lineno);
    }
    break;
  case NUMBER:
  case STRCONST:
    p->width = 1;
    break;
  case ARRAYREF:
    if (!(sym = get_sym_decl(t,p->d->iname))) {
      sprintf(s,"Unknown array or tablemap '%s'.",p->d->iname);
      parse_error_line(s,p->lineno);
    } else {
      sym->mark = 1;
      p->d->sym = sym;
    }
    check_sym_ref_expr(sa,t,p->left); /* bracketed expr */
    if (p->left->width != 1) {
      sprintf(s,"Index expression must be single-valued.\n");
      parse_error_line(s,p->lineno);
    }
    p->width = 1;
    break;
    
  case OPARRAY:
    if (!(sym = get_sym_decl(t,p->d->iname))) {
      sprintf(s,"Opcode array '%s' requires definition.",p->d->iname);
      parse_error_line(s,p->lineno);
    }

    p->oparray_defn = sym;
    sym->mark = 1;

    if (!(sym = get_opcode_decl(sa->all->g->sym,p->d->iname))) {
      sprintf(s,"Unknown opcode '%s'.",p->d->iname);
      parse_error_line(s,p->lineno);
    }
    op = (opcode_decl *)sym->defn;
    p->co_ptr = is_core_opcode(sym->name);
    p->actparam_ct = check_opcode_paramlist(sa,t, p->params,
					    ((opcode_decl *)sym->defn)->params,
					    p->d->iname,p->lineno);
    if (p->actparam_ct > sa->all->g->max_actp_ct)
      sa->all->g->max_actp_ct = p->actparam_ct;
    check_sym_ref_exprlist(sa,t,p->params);
    check_sym_ref_expr(sa,t,p->left);

    if (op->width == -1)
      check_opcode_width(sa,op);
    
    p->defn = sym;
    if (p->left->width != 1) {
      sprintf(s,"Index expression must be single-valued.\n");
      parse_error_line(s,p->lineno);
    }
    p->width = op->width;
    
    break;

  case OPCALL:
    if (!(sym = get_opcode_decl(sa->all->g->sym,p->d->iname))) {
      sprintf(s,"Unknown opcode '%s'.",p->d->iname);
      parse_error_line(s,p->lineno);
    }
    op = (opcode_decl *)sym->defn;
    p->co_ptr = is_core_opcode(sym->name);
    p->actparam_ct = check_opcode_paramlist(sa,t, p->params,((opcode_decl *)sym->defn)->params,
					    p->d->iname,p->lineno);
    if (p->actparam_ct > sa->all->g->max_actp_ct)
      sa->all->g->max_actp_ct = p->actparam_ct;
    check_sym_ref_exprlist(sa,t,p->params);
    if (op->width == -1)
      check_opcode_width(sa,op);
    p->width = op->width;
    p->defn = sym;
    break;
    
  case NOT:
  case UMINUS:
  case LP:
    check_sym_ref_expr(sa,t,p->left);
    p->width = p->left->width;
    break;
  case Q:
    check_sym_ref_expr(sa,t,p->left);
    check_sym_ref_expr(sa,t,p->right);
    check_sym_ref_expr(sa,t,p->extra);
    if (((p->extra->width != p->left->width)
	 && p->extra->width != 1 && p->left->width != 1) ||
	((p->left->width != p->right->width)
	 && p->left->width != 1 && p->right->width != 1)) { 
      sprintf(s,"All subexpressions must be same width, or scalar.\n");
      parse_error_line(s,p->lineno);
    }
    p->width = MAX(MAX(p->left->width,p->right->width),p->extra->width);
    break;
  case LEQ:
  case GEQ:
  case EQEQ:
  case NEQ:
  case GT:
  case LT:
  case AND:
  case OR:
  case PLUS:
  case MINUS:
  case STAR:
  case SLASH:
    check_sym_ref_expr(sa,t,p->left);
    check_sym_ref_expr(sa,t,p->right);
    if (p->left->width != p->right->width && p->left->width != 1
	&& p->right->width != 1) {
      sprintf(s,"Both subexpressions must be same width, or scalar.\n");
      parse_error_line(s,p->lineno);
    }
    p->width = MAX(p->left->width,p->right->width);
    break;
  default:
    sprintf(s,"Unknown expr type in check_sym_ref_expr (%d)!",p->op);
    interror(s);
  }
}

void check_sym_ref_block(sa_decoder *sa,symtable *t, block *b) {
  if (!b->st) return;
  for (;b;b=b->next)
    check_sym_ref_statement(sa,t,b->st);
}

int check_opcode_width_block(sa_decoder *sa, symtable *t, block *b);

void check_opcode_width(sa_decoder *sa,opcode_decl *op) {
  
  op->width = check_opcode_width_block(sa, op->sym, op->code);
}

int check_opcode_width_block(sa_decoder *sa, symtable *t, block *b) {
  int ct,width=0;
  exprlist *el;
  
  for (;b && b->st;b = b->next) {
    if (b->st->type == RETURN) {
      ct = 0; for (el=b->st->params;el && el->p;el=el->next) {
	check_sym_ref_expr(sa,t,el->p);
	ct += el->p->width;
      }
      return(ct);
    }
    if (b->st->type == IF || b->st->type == WHILE || b->st->type == ELSE) {
      width = check_opcode_width_block(sa, t, b->st->b);
      if (width) return(width);
    }
    if (b->st->type == ELSE) {
      width = check_opcode_width_block(sa, t, b->st->elseb);
      if (width) return(width);
    }
  }
  
  return(0);
}
  

symtable *remove_unused_symbols(sa_decoder *sa,instr_decl *id, symtable *t) {
  symtable *first, *cur, *last, *hold;
  int ct = 0;

  first = t;

  cur=first; last = NULL;
  
  while (cur) {
    if (!strcmp(cur->s->name,"input"))
      cur->s->width = 0; /* horrible hack */
    if (!cur->s->mark && !(t == sa->all->g->sym)) { /* unused */
      first = remove_sym_table(first,last,&cur);
    }
    else if (cur->s->type != OPARRAY) {
      cur->s->offset = ct; 
      ct += cur->s->width;
      last = cur;
      cur = cur->next;
    }
    else {
      last = cur;
      cur = cur->next;
    }
  }
  if (id) id->framesize = ct;
  if (t == sa->all->g->sym)
    sa->all->g->framesize = ct;
  return(first);
}

symtable *remove_sym_table(symtable *first, symtable *last, symtable **cur) {
  symtable *hold;
  
  if (*cur == first)
    first = (*cur)->next;
  else if (last)
    last->next = (*cur)->next;

  hold = (*cur)->next;
  free((*cur)->s->name);
  free((*cur)->s);
  free((*cur));

  *cur = hold;
  return first;
}
	
	
void check_sym_ref_statement(sa_decoder *sa, symtable *t, statement *st) {
  char s[500];
  bustable *bd;
  symbol *sym;
  int ct;
  exprlist *el;
  namelist *nl;
    
  if (!st) return;
  switch (st->type) {
  case EQ:
    if (st->lvalue && st->lvalue->op == ARRAYREF) {
      check_sym_ref_expr(sa,t,st->lvalue);
    } else if (st->lvalue && st->lvalue->op == IDENT) {
      if (!(sym = get_var_decl(t,st->lvalue->d->iname))) {
	sprintf(s,"Unknown symbol or illegal lvalue '%s'", st->lvalue->d->iname);
	parse_error_line(s,st->lineno);
      }
      else if (st->lvalue) {
	sym->mark = 1;
	st->lvalue->d->sym = sym;
	st->lvalue->width = sym->width;
      }
      
    } else if (st->lvalue) {
      parse_error_line("Illegal lvalue.",st->lineno);
    }
    check_sym_ref_expr(sa,t,st->expr);
    if (st->lvalue && st->lvalue->width != st->expr->width && st->expr->width != 1) {
      parse_error_line("Lvalue and expression must have equal width.\n",st->lineno);
    }
    break;
  case IF:
    check_sym_ref_expr(sa,t,st->expr);
    if (st->expr->width != 1) {
      sprintf(s,"Guard expression must be single-valued.\n");
      parse_error_line(s,st->lineno);
    }

    check_sym_ref_block(sa,t,st->b);
    break;
  case ELSE:
    check_sym_ref_expr(sa,t,st->expr);
    if (st->expr->width != 1) {
      sprintf(s,"Guard expression must be single-valued.\n");
      parse_error_line(s,st->lineno);
    }

    check_sym_ref_block(sa,t,st->b);
    check_sym_ref_block(sa,t,st->elseb);
    break;
  case WHILE:
    
    check_sym_ref_expr(sa,t,st->expr);
    if (st->expr->width != 1) {
      sprintf(s,"Guard expression must be single-valued.\n");
      parse_error_line(s,st->lineno);
    }
    check_sym_ref_block(sa,t,st->b);
    break;
  case OUTPUT:
    
    /* check number of channels here? */
    for (ct=0,el=st->params;el;el=el->next,ct++) ;
    
    check_sym_ref_exprlist(sa,t,st->params);
    break;
  case SPATIALIZE:
    for (ct=0,el=st->params;el;el=el->next,ct++) ;
    
    if (ct != 4)
      parse_error_line("bad call: spatialize(aexp, azim, elev, dist)",st->lineno);
    
    check_sym_ref_exprlist(sa,t,st->params);
    for (el=st->params;el;el=el->next)
      if (el->p->width != 1)
	parse_error_line("Spatialize expressions must be single-valued.\n",st->lineno);
    
    break;
    case SFSYNTH:
      for (ct=0,el=st->params;el;el=el->next,ct++) ;
      if (ct != 3)
	parse_error_line("bad call: \n sbsynth(bank, note, vel; outbus[, revbus, chorbus]\n         [; preset, channel,midibank])",st->lineno);
      for (ct=0,nl=st->sfbusses;nl && nl->n;nl=nl->next,ct++)
      if (!(bd = get_bus(sa->all->g->bus,nl->n->name))) {
	sprintf(s,"Unknown bus: '%s'.",nl->n->name);
	parse_error_line(s,st->lineno);
      }
      else
	set_bus_width(bd,1);  /* first one reset below */
    if (st->sfbusses && st->sfbusses->n)
      bd = get_bus(sa->all->g->bus,st->sfbusses->n->name);
    else
      bd = NULL;
    if (bd && bd->width > 2) {
      sprintf(s,"Number of channels in bus '%s' routings don't match (must be stereo for sbsynth.",nl->n);
      parse_error_line(s,st->lineno);
    }
    set_bus_width(bd,2);
    if (ct < 1 || ct > 3)
     parse_error_line("bad call: \n sbsynth(bank, note, vel; outbus[, revbus, chorbus]\n          [; preset, channel, midibank])",st->lineno);
    for (ct=0,el=st->sfextra;el;el=el->next,ct++) ;
    if (ct > 3)
      parse_error_line("bad call: \n sbsynth(bank, note, vel; outbus[, revbus, chorbus]\n         [; preset, channel, midibank])",st->lineno);
    check_sym_ref_exprlist(sa,t,st->params);
    for (el=st->params;el;el=el->next)
      if (el->p->width != 1)
	parse_error_line("Sbsynth expressions must be single-valued.\n",st->lineno);
    check_sym_ref_exprlist(sa,t,st->sfextra);
    for (el=st->sfextra;el;el=el->next)
      if (el->p->width != 1)
	parse_error_line("Sbsynth expressions must be single-valued.\n",st->lineno);
    break;
  case OUTBUS:
    if (!(bd = get_bus(sa->all->g->bus,st->bus))) {
      sprintf(s,"Unknown bus: '%s'.",st->bus);
      parse_error_line(s,st->lineno);
    } else {
      check_sym_ref_exprlist(sa,t,st->params);
    
      /* check the number of channels */
      /* count number of channels in this call to outbus */
      for (ct=0,el = st->params;el;el=el->next,ct+=el->p->width) ;

      if (bd->width != 0 && ct != 1 && bd->width != ct) {
	sprintf(s,"Number of channels in bus '%s' routings don't match.",
		st->bus);
	parse_error_line(s,st->lineno);
      }
      if (bd->width == 0 || bd->width == 1)
	set_bus_width(bd,ct);
    }
    break;
  case RETURN:
    check_sym_ref_exprlist(sa,t,st->params);
    break;
  case TURNOFF:
    /* no parameters to turnoff */
    break;
  case EXTEND:
    check_sym_ref_expr(sa,t,st->expr);
    if (st->expr->width != 1)
      parse_error_line("Extend expression must be single-valued.\n",st->lineno);
    break;
  case INSTR:
    if (!get_instr_decl(sa->all->g->sym,st->iname)) {
      sprintf(s,"Undeclared instrument '%s'.",st->iname);
      parse_error_line(s,st->lineno);
    }
    check_sym_ref_exprlist(sa,t,st->params);
    for (el = st->params; el && el->p; el=el->next)
      if (el->p->width != 1)
	parse_error_line("Instr expressions must be single-valued.\n",st->lineno);
    break;
  default:
    interror("bad statement type in check_sym_ref_statement()");
  }
}

int check_opcode_paramlist(sa_decoder *sa,symtable *t, exprlist *el,
			   formalparam_list *fpl, char *opname, int lineno) {
  /* this is the only tricky one to syntax-check: */
  /* need to check expr by expr to allow for dynamic wavetables */
  /* need to match up number of parameters except for varargs & optional CO's */
  char s[1000];
  int ct = 0;
  int paramct = 0;
  symbol *sym;
	
  while (el && el->p) {
    paramct++;
    if (!fpl) { /* ran out of formal before actual parameters */
      /* see if its a built-in varargs */
      if (!opcode_is_varargs(opname)) {
	sprintf(s,"Opcode '%s' expects only %d parameters.",opname,ct);
	parse_error_line(s,lineno);
      }
    }
    if (el->p->op == IDENT)
      if (!(sym = get_sym_decl(t,el->p->d->iname)))
				/* unknown identifier in opcode call */
	if (fpl && fpl->fp && fpl->fp->type == TABLE) { /* okay */
	  sprintf(s,"Unknown identifier '%s' assumed dynamic wavetable.",
		  el->p->d->iname);
	  warn_line(s,lineno);
	  el->p->d->sym = NULL;
	} else { /* not okay */
	  sprintf(s,"'%s': Unknown identifier.",el->p->d->iname);
	  parse_error_line(s,lineno);
	}
      else {
	sym->mark = 1;
	el->p->d->sym = sym;
	el->p->width = sym->width;
      }
    else check_sym_ref_expr(sa,t,el->p);
    if (el && el->p && fpl && fpl->fp && el->p->width != fpl->fp->n->width)
      parse_error_line("Mismatch in array width for opcode call.\n",lineno);
    el = el->next; if (fpl) fpl = fpl->next; ct++;
  }
  if (fpl && fpl->fp && fpl->fp->type > 0) {
    /* still have formal parameters */
		
    int xct = ct;
    while (fpl && fpl->fp) { fpl = fpl->next; xct++; }
		
    sprintf(s,"Call to opcode '%s' with only %d parameters (%d expected).",
	    opname,ct,xct);
    parse_error_line(s,lineno);
  }
  return(paramct);
}
  
void check_instr_ref_namelist(symtable *t, namelist *nl) {
  char s[800];

  if (!nl->n) return;
  
  for (;nl;nl=nl->next)
    if (!get_instr_decl(t,nl->n->name)) {
      sprintf(s,"'%s': unknown instrument for route/sequence.",nl->n->name);
      parse_error_line(s,nl->lineno);
    }
}

void check_instr_ref_name(symtable *t, char *name) {
  char s[800];

  if (!name) return;

  if (!get_instr_decl(t,name)) {
    sprintf(s,"'%s': unknown instrument.",name);
    parse_error(s);
  }
}

void check_bus(bustable *bt, char *name) {
  char s[800];
  
  if (!get_bus(bt,name)) {
    sprintf(s,"'%s': unknown bus.",name);
    parse_error(s);
  }
}

void attach_route(sa_decoder *sa,symtable *t, char *busname, namelist *nl) {
  bustable *bus;
  instr_decl *id;
  char s[945];
  int ct = 0;
  namelist *first = nl;

  bus = get_bus(sa->all->g->bus,busname);
  
  if (!nl->n) return;

  for (;nl;nl=nl->next) {
    id = (instr_decl *)(get_instr_decl(t,nl->n->name)->defn);
    if (id->route) {
      sprintf(s,"Rerouting instrument '%s' from bus '%s' to bus '%s'",
	      nl->n->name,id->route->name,bus->name);
      warn_line(s,nl->lineno);
    }
    id->route = bus;
    ct += sa->all->g->outchan;
  }
  if (bus->width && bus->width != 1 && bus->width != ct) {
    sprintf(s,"Number of channels routed to bus '%s' don't match",bus->name);
    parse_error_line(s,first->lineno);
  }
  bus->width = ct;
    
}

symbol *get_sym_decl(symtable *t, char *name) {

  if (!t || !t->s) return NULL;
  
  for (;t;t=t->next)
    if (!strcmp(t->s->name,name)) return t->s;
  return NULL;
}

symbol *get_sym_decl_last(symtable *t, char *name) {
  symbol *hold = NULL;

  if (!t || !t->s) return NULL;
  
  for (;t;t=t->next)
    if (!strcmp(t->s->name,name)) hold = t->s;
  return hold;
}

symbol *get_var_decl(symtable *t, char *name) {
  symbol *s;

  s = get_sym_decl(t,name);
  if (s && (s->type == XSIG || s->type == ASIG || s->type == KSIG ||
	    s->type == IVAR))
    return s;
  else return NULL;
}

symbol *get_opcode_decl(symtable *t, char *name) {
  symbol *s;

  s = get_sym_decl(t,name);
  if (s && (s->type == OPCODE || s->type == AOPCODE || s->type == KOPCODE ||
	    s->type == IOPCODE || s->type == SPECIALOP))
    return s;
  else return NULL;
}

symbol *get_instr_decl(symtable *t, char *name) {
  symbol *s;

  s = get_sym_decl(t,name);
  if (s && (s->type == INSTR))
    return s;
  else return NULL;
}

symbol *get_table_decl(symtable *t, char *name) {
  symbol *s;

  s = get_sym_decl(t,name);
  if (s && (s->type == TABLE))
    return s;
  else return NULL;
}

bustable *get_bus(bustable *bt, char *name) {
  if (!bt || !bt->name) return NULL;

  for (;bt;bt=bt->next)
    if (!strcmp(bt->name,name)) return bt;
  return NULL;
}


void dump_sym_table(symtable *t) {

  for (;t;t=t->next) {
    switch(t->s->type) {
    case IVAR: printf("ivar "); break;
    case KSIG: printf("ksig "); break;
    case ASIG: printf("asig "); break;
    case XSIG: printf("xsig "); break;
    case TABLE: printf("table "); break;
    case OPARRAY: printf("oparray "); break;
    }
    printf("%s [%d] --> %d\n",t->s->name,t->s->width,t->s->offset);
  }
}

#if 0
int get_instr_localspace(sa_decoder *sa, instr_decl *id) {
  /* figure out how much space (including opcodes) each instance of
     this instr will need */
  symtable *sym;
  int total = 0;

  for (sym=id->sym;sym;sym=sym->next) {
    ct += sym->s->width == 0 ? inchan : ptr->s->width;

      
    
    
    for (
#endif
