/* $Id: saol_parser.c,v 1.5 1997/11/05 20:11:14 eds Exp $ */
/* $Log: saol_parser.c,v $
 * Revision 1.5  1997/11/05  20:11:14  eds
 * Added tablemaps.
 *
 * Revision 1.4  1997/11/05  15:01:35  eds
 * Added vector parameters/return values/operators.
 *
 * Revision 1.3  1997/10/03  15:11:48  eds
 * Added sfsynth 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>
#ifdef _WIN32
#include <string.h>
#else
#include <strings.h>
#endif
#include "y.tab.h"
#include "saol.h"
#include <malloc.h>

extern int yyline;

void interror(char *s) {
  printf("\nInternal compiler error: %s\n",s);
  *((char *)0) = 1; /* seg fault */
}

orc *new_orc(void) {
  orc *o;

  PROT_MAL(o,orc,new_orc);
  o->g = NULL;
  o->il = NULL;
  o->op = NULL;

  return o;
}


global_block *new_gblock(void) {
  global_block *gb;

  PROT_MAL(gb,global_block,new_gblock);
  gb->d = NULL;
  gb->next = NULL;
  return(gb);
}

void add_gblock(global_block *gb, global_decl *d) {
  global_block *newb,*last;
  if (!gb->d) /* first decl */
    gb->d = d;
  else {
    PROT_MAL(newb,global_block,add_gblock);
    newb->d = d;
    newb->next = NULL;
    for (; gb; last = gb,gb = gb->next) ;
    last->next = newb;
  }
}

global_decl *new_global(long type) {
  global_decl *g;

  PROT_MAL(g,global_decl,new_global);
  g->type = type;
  g->rtparam = 0;
  g->vardecl = NULL;
  g->senddef = NULL;
  g->routedef = NULL;
  g->inmoddef = NULL;
  g->seqdef = NULL;
  g->lineno = yyline;
  return(g);
}

void set_global_rtparam(global_decl *g, long param) {
  g->rtparam = param;
}

void set_global_senddef(global_decl *g, senddef *s) {
  g->senddef = s;
}

void set_global_routedef(global_decl *g, routedef *r) {
  g->routedef = r;
}

void set_global_inputmod(global_decl *g, inputmod *in) {
  g->inmoddef = in;
}

void set_global_seqdef(global_decl *g, seqdef *s) {
  g->seqdef = s;
}

void set_global_vardecl(global_decl *g, vardecl *v) {
  g->vardecl = v;
}

instr_list *new_instr_list(void) {
  instr_list *il;

  PROT_MAL(il,instr_list,new_instr_list);
  il->i = NULL;
  il->next = NULL;
  return(il);
}

void add_instr_list(instr_list *il, instr_decl *i) {
  instr_list *newp,*last;

  if (!il->i)
    il->i = i;
  else {
    PROT_MAL(newp,instr_list,add_instr_list);
    newp->next = NULL;
    newp->i = i;
    for (; il; last=il, il = il->next);
    last->next = newp;
  }
}

opcode_list *new_opcode_list(void) {
  opcode_list *op;

  PROT_MAL(op,opcode_list,new_opcode_list);
  op->o = NULL;
  op->next = NULL;
  return(op);
}

void add_opcode_list(opcode_list *op, opcode_decl *o) {
  opcode_list *newp,*last;
  if (!op->o)
    op->o = o;
  else {
    PROT_MAL(newp,opcode_list,add_opcode_list);
    newp->next = NULL;
    newp->o = o;
    for (; op; last=op, op = op->next);
    last->next = newp;
  }
}

instr_decl *new_instr_decl(char *name, namelist *pf, vardecl_list *local,
			   block *code) {
  instr_decl *il;

  PROT_MAL(il,instr_decl,new_instr_decl);
  il->name = name;
  il->width = 0;
  il->params = pf;
  il->localvars = local;
  il->code = code;
  il->route = NULL;
  il->n_opcodes = 0;
  return(il);
}

opcode_decl *new_opcode_decl(char *name, formalparam_list *fp,
			     vardecl_list *local, block *code, long type) {
  opcode_decl *op;

  PROT_MAL(op,opcode_decl,new_opcode_decl);
  op->name = name;
  op->params = fp;
  op->localvars = local;
  op->code = code;
  op->type = type;
  op->rate = 0;
  op->minxsigrate = 0;
  op->width = -1;
  return(op);
}

template_decl *new_template_decl(namelist *names, namelist *params,
				 namelist *tvars, mapblock *map,
				 vardecl_list *local, block *code) {
  template_decl *t;

  PROT_MAL(t,template_decl,new_template_decl);
  t->names = names;
  t->params = params;
  t->tvars = tvars;
  t->mapping = map;
  t->localvars = local;
  t->code = code;
  return(t);
}

mapblock *new_mapblock(terminal_list *tl) {
  mapblock *m;

  PROT_MAL(m,mapblock, new_mapblock);
  m->tl = tl;
  return(m);
}

void add_mapblock(mapblock *m, terminal_list *tl) {
  mapblock *newp, *last;

  PROT_MAL(newp, mapblock, add_mapblock);
  newp->tl = tl;
  newp->next = NULL;

  for (; m; last=m,m=m->next);
  last->next = newp;
}

terminal_list *new_terminal_list(terminal *t) {
  terminal_list *tl;

  PROT_MAL(tl,terminal_list,new_terminal_list);
  tl->t = t;
  return(tl);
}
  
void add_terminal_list(terminal_list *tl, terminal *t) {
  terminal_list *newp,*last;

  PROT_MAL(newp,terminal_list,new_terminal_list);
  newp->t = t;
  newp->next = NULL;

  for (; tl; last=tl, tl=tl->next);
  last->next = newp;
} 

vardecl_list *new_vardecl_list(void) {
  vardecl_list *vdl;

  PROT_MAL(vdl,vardecl_list,new_vardecl_list);
  vdl->v = NULL;
  vdl->next = NULL;
  return(vdl);
}

vardecl *new_vardecl(long type, long taglist, namelist *nl) {
  vardecl *v;
  
  PROT_MAL(v,vardecl,new_vardecl);
  v->type = type;
  v->imported = (taglist == IMPORTS || taglist == IMPEXP);
  v->exported = (taglist == EXPORTS || taglist == IMPEXP);
  v->nl = nl;
  v->tablemap_name = NULL;
  v->t = NULL;
  v->lineno = yyline;
  return(v);
}

void set_vardecl_tabledef(vardecl *v, tabledef *t) {
  v->t = t;
}

tabledef *new_tabledef(char *ident, char *gen, exprlist *params) {
  tabledef *t;

  PROT_MAL(t,tabledef,new_tabledec);
  t->name = ident;
  t->gen = gen;
  t->params = params;
  return(t);
}

namelist *new_namelist(name *v) {
  namelist *nl;

  PROT_MAL(nl,namelist,new_namelist);
  nl->next = NULL;
  nl->n = v;
  nl->lineno = yyline;
  return(nl);
}

void add_namelist(namelist *nl, name *v) {
  namelist *newnl,*last;

  if (!nl->n)
    nl->n = v;
  else {
    PROT_MAL(newnl,namelist,add_namelist);
    newnl->next = NULL;
    newnl->n = v;
    newnl->lineno = yyline;
    for (; nl; last = nl,nl=nl->next);
    last->next = newnl;
  }
}

name *new_name(char *ident,int width) {
  name *v;

  PROT_MAL(v,name,add_namelist);
  v->name = ident;
  v->width = width;
  return(v);
}

formalparam_list *new_formalparam_list(void) {
  formalparam_list *fpl;

  PROT_MAL(fpl,formalparam_list,new_formalparam_list);
  fpl->fp = NULL;
  fpl->next = NULL;
  return(fpl);
}

void add_formalparam_list(formalparam_list *fpl, formalparam *fp) {
  formalparam_list *newp, *last;

  if (!fpl->fp)
    fpl->fp = fp;
  else {
    PROT_MAL(newp,formalparam_list,add_formalparam_list);
    newp->next = NULL;
    newp->fp = fp;
    for (; fpl; last = fpl, fpl = fpl->next);
    last->next = newp;
  }
}

formalparam *new_formalparam(long type, name *v) {
  formalparam *fp;

  PROT_MAL(fp,formalparam,new_formalparam);
  fp->type = type;
  fp->lineno = yyline;
  fp->n = v;
  return(fp);
}

routedef *new_routedef(char *bus, namelist *instrs) {
  routedef *r;
  
  PROT_MAL(r, routedef,new_routedef);
  r->bus = bus;
  r->instrs = instrs;
  return(r);
}
   
senddef *new_senddef(char *instr,exprlist *pfields,namelist *busses) {
  senddef *s;

  PROT_MAL(s,senddef,new_senddef);
  s->instr= instr;
  s->pfields = pfields;
  s->busses = busses;
  return(s);
}

seqdef *new_seqdef(namelist *seqlist) {
  seqdef *s;

  PROT_MAL(s,seqdef,new_seqdef);
  s->seqlist = seqlist;
  return(s);
}

inputmod *new_inmod(char *instr,exprlist *params) {
  inputmod *inmod;

  PROT_MAL(inmod,inputmod,new_inmod);
  inmod->instr = instr;
  inmod->params = params;
  return(inmod);
}

block *new_block(void) {
  block *b;

  if (!(b = (block *)malloc(sizeof(block)))) {
    interror("malloc() failure in new_block()\n");
  }
  b->st = NULL;
  b->next = NULL;
  
  return(b);
}

void add_block(block *b, statement *st) {
  block *last,*newb;
  
  if (!b->st) { /* null block */
    b->st = st;
  } else {
    if (!(newb = (block *)malloc(sizeof(block)))) {
      interror("malloc() failure in add_block()\n");
    }
    newb->st = st;
    newb->next = NULL;
    for (; b; last=b,b=b->next);
    last->next = newb;
  }
}

statement *new_statement(long type, expr *e, block *b) {
  statement *st;
  
  if (!(st = (statement *)malloc(sizeof(statement)))) {
    interror("malloc() failure in add_block()\n");
  }
  st->type = type;
  st->rate = 0;
  st->expr = e;
  st->b = b;
  st->elseb = NULL;
  st->lvalue = NULL;
  st->params = NULL;
  st->sfbusses = NULL;
  st->sfextra = NULL;
  st->bus = NULL;
  st->iname = NULL;
  st->lineno = yyline;

  return(st);
}

void set_statement_params(statement *st, exprlist *el) {
  st->params = el;
}

void set_statement_lvalue(statement *st, expr *l) {
  st->lvalue = l;
}

void set_statement_iname(statement *st, char *iname) {
  st->iname = iname;
}

void set_statement_else(statement *st, block *b) {
  st->elseb = b;
}

void set_statement_bus(statement *st, char *t) {
  st->bus = t;
}
    
expr *new_expr(expr *left, expr *right, expr *extra, int type) {
  expr *p;

  if (!(p = (expr *)malloc(sizeof(expr)))) {
    interror("malloc() failure in new_expr()\n");
  }
  p->op = type;
  p->d = NULL;
  p->params = NULL;
  p->left = left;
  p->right = right;
  p->extra = extra;
  p->lineno = yyline;
  p->defn = NULL;
  p->opdummy = NULL;
  p->oparray_defn = NULL;
  p->op_offset = 0;
  p->width = 0;
  return(p);
}

void set_expr_data(expr *p, terminal *d) {
  p->d = d;
}

void set_expr_params(expr *p, exprlist *l) {
  p->params = l;
}

terminal *new_terminal(int type, char *cont) {
  terminal *t;

  if (!(t = (terminal *)malloc(sizeof(terminal)))) {
    interror("malloc() failure in new_terminal()\n");
  }
  t->type = type;
  t->cval = 0;
  t->iname = t->csval = NULL;
  
  switch (type) {
  case IDENT: /* gotta copy string */
    t->iname = cont;
    break;
  case NUMBER:
    t->cval = atof(cont);
    break;
  case STRCONST:
    t->csval = cont;
    break;
  }
  return t;
}

exprlist *new_exprlist(expr *p) {
  exprlist *e;

  if (!(e = (exprlist *)malloc(sizeof(exprlist)))) {
    interror("malloc() failure in new_exprlist");
  }

  e->p = p;
  e->next = NULL;
  return(e);
}

void add_exprlist(exprlist *e, expr *p) {
  exprlist *last,*new;

  if (!(new = (exprlist *)malloc(sizeof(exprlist)))) {
    interror("malloc() failure in add_exprlist");
  }

  new->p = p;
  new->next = NULL;
  for (; e ; last = e, e=e->next) ;
  last->next = new;
}
    
vardecl_list *copy_vardecl_list(vardecl_list *vdl) {
  vardecl_list *newl,*vdlp;

  if (!vdl) return NULL;
  newl = new_vardecl_list(); 
  for (vdlp=vdl;vdlp;vdlp=vdlp->next)
    add_vardecl_list(newl,copy_vardecl(vdlp->v));
  return(newl);
}

namelist *copy_namelist(namelist *nl) {
  namelist *newl, *nlp;

  if (!nl) return NULL;
  newl = new_namelist(copy_name(nl->n));
  
  for (nlp=nl->next; nlp; nlp=nlp->next) 
    add_namelist(newl,copy_name(nlp->n));
  return(newl);
}

vardecl *copy_vardecl(vardecl *vd) {
  vardecl *newvd;
  long tag;

  if (!vd) return NULL;
  if (vd->exported) 
    if (vd->imported)
      tag = IMPEXP;
    else
      tag = EXPORTS;
  else
    if (vd->imported)
      tag = IMPORTS;
    else
      tag = NOTAG;
  
  newvd = new_vardecl(vd->type, tag, copy_namelist(vd->nl));
  set_vardecl_tabledef(newvd,copy_tabledef(vd->t));
  return(newvd);
}

name *copy_name(name *v) {
  name *newv;

  if (!v) return NULL;
  newv = new_name(strdup(v->name),v->width);
  return(newv);
}

block *copy_block(block *b) {
  block *newb, *bp;
  statement *news;

  if (!b) return NULL;
  
  newb = new_block();
  for (bp = b; bp; bp = bp->next) {
    news = copy_statement(bp->st);
    add_block(newb,news);
  }
  return(newb);
}

tabledef *copy_tabledef(tabledef *t) {
  tabledef *newt;

  if (!t) return NULL;
  
  newt = new_tabledef(strdup(t->name),strdup(t->gen),copy_exprlist(t->params));
  return(newt);
}

statement *copy_statement(statement *st) {
  statement *newst;

  if (!st) return NULL;
  
  newst = new_statement(st->type,copy_expr(st->expr),copy_block(st->b));
  set_statement_params(newst,copy_exprlist(st->params));
  if (st->iname) set_statement_iname(newst,strdup(st->iname));
  set_statement_lvalue(newst,copy_expr(st->lvalue));
  set_statement_else(newst,copy_block(st->elseb));
  if (st->bus) set_statement_bus(newst,strdup(st->bus));
  newst->rate = st->rate;
  newst->sfbusses = copy_namelist(st->sfbusses);
  newst->sfextra = copy_exprlist(st->sfextra);
  return (newst);
}

expr *copy_expr(expr *e) {
  expr *newe;

  if (!e) return NULL;
  newe = new_expr(copy_expr(e->left),copy_expr(e->right),copy_expr(e->extra),
		 e->op);
  set_expr_data(newe,copy_terminal(e->d));
  set_expr_params(newe,copy_exprlist(e->params));
  newe->defn = e->defn;
  newe->oparray_defn = e->oparray_defn;
  newe->op_offset = e->op_offset;
  newe->co_ptr = e->co_ptr;
  newe->rate = e->rate;
  newe->actparam_ct = e->actparam_ct;
  newe->width = e->width;
  return(newe);
}

exprlist *copy_exprlist(exprlist *el) {
  exprlist *newel, *ep;

  if (!el) return NULL;
  newel = new_exprlist(copy_expr(el->p));
  for (ep=el->next;ep;ep=ep->next)
    add_exprlist(newel,copy_expr(ep->p));
  return (newel);
}

terminal *copy_terminal(terminal *t) {
  terminal *newt;
  char *s,buf[80];

  if (!t) return NULL;
  switch ((int)t->type) {
  case STRCONST:
    s = strdup(t->csval);
    break;
  case IDENT:
    s = strdup(t->iname);
    break;
  case NUMBER:
    sprintf(buf,"%.16lf",t->cval);
    s = strdup(buf);
    break;
  }
  newt = new_terminal(t->type,s);
  newt->sym = t->sym;
  return (newt);
}

route_list *new_route_list(routedef *r) {
  route_list *rl;

  PROT_MAL(rl,route_list,new_route_list);
  rl->r = r;
  rl->next = NULL;
  return(rl);
}

send_list *new_send_list(senddef *s) {
  send_list *sl;

  PROT_MAL(sl,send_list,new_send_list);
  sl->s = s;
  sl->next = NULL;
  return(sl);
}

sequence_list *new_sequence_list(seqdef *s) {
  sequence_list *seql;

  PROT_MAL(seql,sequence_list,new_sequence_list);
  seql->s = s;
  seql->next = NULL;
  return(seql);
}

void add_vardecl_list(vardecl_list *pl,vardecl *p) {
  vardecl_list *newp,*last;

  if (!pl->v)
    pl->v = p;
  else {
    PROT_MAL(newp,vardecl_list,add_vardecl_list);
    newp->v = p;
    newp->next = NULL;
    for (;pl;last=pl,pl=pl->next);
    last->next = newp;
  }
}

void add_route_list(route_list *rl, routedef *r) {
  route_list *newp, *last;

  PROT_MAL(newp,route_list,add_route_list);
  newp->r = r;
  newp->next = NULL;
  for (;rl;last=rl,rl=rl->next);
  last->next = newp;
}

void add_send_list(send_list *pl,senddef *p) {
  send_list *newp,*last;

  PROT_MAL(newp,send_list,add_send_list);

  newp->s = p;
  newp->next = NULL;
  for (;pl;last=pl,pl=pl->next);
  last->next = newp;
}

void add_sequence_list(sequence_list *pl,seqdef *p) {
  sequence_list *newp,*last;

  PROT_MAL(newp,sequence_list,add_sequence_list);

  newp->s = p;
  newp->next = NULL;
  for (;pl;last=pl,pl=pl->next);
  last->next = newp;
}
  
extern sa_decoder *cur_sa; /* for parsing only */

void *ptr_index(long i) {
  ptr_index_list *p = cur_sa->all_ptrs;

  while (p && (p->index != i)) p=p->next;
  if (p)
    return(p->ptr);
  else interror("Pointer index not found!");
  return NULL;
}

long add_ptr_index(void *ptr) {
  ptr_index_list *p = cur_sa->all_ptrs,*last = NULL,*newp;

  for (;p;last = p,p=p->next);
  if (!(newp = (ptr_index_list *)malloc(sizeof(ptr_index_list))))
    interror("malloc() failure in add_ptr_index()\n");
  newp->next = NULL;

  if (last) {
    last->next = newp;
    newp->index = last->index + 1;
  }
  else {
    cur_sa->all_ptrs = newp;
    newp->index = 0;
  }
  
  newp->ptr = ptr;
  return(newp->index);
}

void print_block(block *b) {
  if (!b->st) {
    printf("<null block>\n");
  } else {
    for (; b; b=b->next) 
      print_statement(b->st);
  }
}

void print_statement(statement *st) {
  namelist *nl;
  
  if (!st)
    printf("<NULL> ");
  else switch((int)st->type) {
  case EQ: /* assignment */
    print_expr(st->lvalue);
    printf(":= ");
    print_expr(st->expr);
    break;
  case IF:
    printf("if ( ");
    print_expr(st->expr);
    printf(") { \n");
    print_block(st->b);
    printf("}");
    break;
  case ELSE:
    printf("if-else ( ");
    print_expr(st->expr);
    printf(") { \n");
    print_block(st->b);
    printf("} else {\n");
    print_block(st->elseb);
    printf("}");    
    break;
  case WHILE:
    printf("while ( ");
    print_expr(st->expr);
    printf(") { \n");
    print_block(st->b);
    printf("}");
    break;
  case OUTPUT:
    printf("output ( ");
    print_exprlist(st->params);
    printf(")");
    break;
  case SPATIALIZE:
    printf("spatialize ( ");
    print_exprlist(st->params);
    printf(")");
    break;
  case SFSYNTH:
    printf("sfsynth (");
    print_exprlist(st->params);
    printf(" ; ");
    for (nl=st->sfbusses;nl && nl->n; nl=nl->next) {
      printf("%s",nl->n->name);
      if (nl->next && nl->next->n)
	printf(", ");
    }
    printf(" ; ");
    print_exprlist(st->sfextra);
    printf(")");
    break;
  case OUTBUS:
    printf("outbus [%s] ( ",st->bus);
    print_exprlist(st->params);
    printf(")");
    break;
  case EXTEND:
    printf("extend (");
    print_expr(st->expr);
    printf(")");
    break;
  case INSTR:
    printf("start instr [%s] (",st->iname);
    print_exprlist(st->params);
    printf(")");
    break;
  case RETURN:
    printf("return ( ");
    print_exprlist(st->params);
    printf(")");
    break;
  case TURNOFF:
    printf("turnoff");
    break;
  default:
    interror("<unknown statement>");
  }
  printf("\n");
}
    
void print_terminal(terminal *t) {
  if (!t)
    printf("<null>");
  else switch((int)t->type) {
  case IDENT:
    printf("%s ",t->iname);
    break;
  case NUMBER:
    printf("%lf ",t->cval);
    break;
  case STRCONST:
    printf("""%s"" ",t->csval);
    break;
  }
}
  
void print_expr(expr *p) {

  if (!p)
    printf("<NULL> ");
  else switch (p->op) {
  case IDENT:
    printf("%s (%d) ",p->d->iname,p->width);
    
    break;
  case NUMBER:
    printf("%.2lf ",p->d->cval);
    break;
  case STRCONST:
    printf("""%s"" ",p->d->csval);
    break;
  case ARRAYREF:
    printf("%s[",p->d->iname);
    print_expr(p->left);
    printf("] (%d) ",p->width);
    break;
  case OPARRAY:
    printf("%s[",p->d->iname);
    print_expr(p->left);
    printf("] (");
    print_exprlist(p->params);
    printf(") ");
    break;
  case OPCALL:
    printf("%s(",p->d->iname);
    print_exprlist(p->params);
    printf(") ");
    break;
  case NOT:
    printf("!");
    print_expr(p->left);
    break;
  case Q:
    printf("(");
    print_expr(p->left);
    printf(" ? ");
    print_expr(p->right);
    printf(" : ");
    print_expr(p->extra);
    printf(") ");
    break;
  case LEQ:
    printf("<= ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case GEQ:
    printf(">= ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case NEQ:
    printf("!= ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case EQEQ:
    printf("== ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case GT:
    printf("> ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case LT:
    printf("< ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case AND:
    printf("&& ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case OR:
    printf("|| ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case PLUS:
    printf("+ ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case MINUS:
    printf("- ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case STAR:
    printf("* ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case SLASH:
    printf("/ ");
    print_expr(p->left);
    print_expr(p->right);
    break;
  case UMINUS:
    printf("u- ");
    print_expr(p->left);
    break;
  case LP:
    print_expr(p->left);
    break;
  default:
    printf("<unknown expression> ");
  }
}

void pen(expr *p) {

  print_expr(p);
  printf("\n");
}

void print_exprlist(exprlist *e) {
  if (!e->p) {
    printf("<null list> ");
    return; /* null list */
  }
  for (; e; e=e->next) {
    print_expr(e->p);
    if (e->next) printf(", ");
  }
}
