view mc-parse.c @ 438:626d705471d5 lazy-branch

Unexecuted code in conditional. Lazy jmp code generation.
author kono
date Mon, 15 Nov 2004 20:33:30 +0900
parents d92786033042
children a531bbf572e3
line wrap: on
line source

/* Micro-C Parser Part */ /* $Id$ */

#include <stdio.h>
#include "mc.h"
#include "mc-parse.h"
#include "mc-codegen.h"
#include "mc-switch.h"
#include "mc-macro.h"
#include "conv/conv.h"

#define FILES 10
#define OUTPUT_FILE_NAME "mcout.s"

extern void exit(int);  /* to avoid stdlib.h inclusion */
#if LONGLONG_CODE
#if 0
extern long long
     strtoll(char * nptr, char ** endptr, int base);
#endif
#endif
#if FLOAT_CODE
extern double strtod(const char *nptr, char **endptr);
#endif

#define HEAP_REPORT 0

NMTBL null_nptr;
NMTBL *fnptr;
NMTBL *nptr,*gnptr;
static NMTBL *htable0[GSYMS];
NMTBL **htable = htable0;
struct cheap *cheap,*cheap0;

static struct cheap *nptr_pool,*nptr_pool0;
static NMTBL *free_nptr_list;

static int current_scope;

char linebuf[LBUFSIZE];
char *chptr;
int args,init_vars;
int *heap;
int heapsize = HEAPSIZE;
int asmf;
int bit_field_disp;
int blabel,dlabel;
int ch;
int chk;
int chptrsave; 
int chsave; 
int cslabel,control;
int csvalue1;
int debug; 
int fields;
int glineno; 
int gtypedefed;
int in_comment; 
int in_quote; 
int labelno,gpc,disp;
int lastexp;
int lfree; 
int lineno; 
int lsrc; 
int retlabel,retpending,retcont;
int pending_jmp;
int struct_return;
int sym,type,mode,stmode;
int typedefed;
int decl_str_init;

NMTBL *local_static_list,*global_list;


struct {int fd,ln;char *name0;int inc;FILE *fcb;} *filep,filestack[FILES];

static NMTBL *decl0(void),*decl1(void),*l_top_search(char *name,int sc);
static int append3(int p,int a1,int a2);
static int expr0(void);
static int expr1(void);
static int expr10(void);
static int expr11(void);
static int expr12(void);
static int expr13(void);
static int expr14(void);
static int expr15(int e1);
static int expr16(int e1);
static int expr2(void);
static int expr3(void);
static int expr4(void);
static int expr5(void);
static int expr6(void);
static int expr7(void);
static int expr8(void);
static int expr9(void);
static int getfree(int n);
static int ndecl0(void);
static int ndecl1(void);
static int postequ(int s1, int s2);
static int sdecl(int s);
static int edecl();
static int adecl(NMTBL *n);
static void code_decl(NMTBL *n);
static void decl(void);
static int typespec(void);
static void docase(void);
static void docomp(int);
static void dodefault(void);
static void dodo(void);
static void dofor(void);
static void dogoto(void);
static void doif(void);
static void dolabel(void);
static void doreturn(void);
static void doswitch(void);
static void dowhile(void);
#if ASM_CODE
static void doasm();
#endif
static void errmsg(void);
static void fdecl(NMTBL *n);
static void getstring(void);
static void init(void);
static void newfile(void);
static void reserve(char *s, int d);
static void reverse(int t1);
static void set_converter(char *s);
static int escape(void);
static void statement(int);
static int typename(void);
static void decl_data_field(int type,NMTBL *n,int offset);
static int decl_data(int t, NMTBL *n,int offset,int skip);
static int typeid(int s);
static int typename(void);
extern NMTBL * get_name_from_chptr();
static NMTBL * hash_search(char *name,struct cheap *scheap,int len,unsigned int hash,int mode);
extern NMTBL * make_local_scope(NMTBL *nlist,NMTBL *nptr1,int sc);
static NMTBL * make_top_scope(NMTBL *nlist,NMTBL *nptr1,int sc);
static void extrn_use(NMTBL *nptr);

static struct cheap * new_cheap();

#if FLOAT_CODE
static double dsymval;
#endif
#if LONGLONG_CODE
static long long  lsymval;
#endif
static int symval;
static int gfree;

static int sdecl_f = 1;
static int stypedecl;

static Converter *conv = &null_converter;
/* Converter *conv = &c_converter; */

static char *ccout = 0;

#define MAX_INCLUDE_PATH_COUNT 10
char *include_path[MAX_INCLUDE_PATH_COUNT];
int include_path_count;
extern char *l_include_path[];

static
char current_file_dir[LBUFSIZE];

static int ac,ac2;
static char **av;
static int cslist;
static int clabel;
static int ilabel;
static int stat_no;

int
main(int argc, char **argv)
{
    int i;

/*
     option handling
 */
    if(argc==1) exit(1);
    lsrc = chk = asmf = 0;
    ac=argc;
    av=argv;
    current_file_dir[0] = 0;
    include_path[include_path_count++] = current_file_dir;
    for (ac2=1; (ac2 < ac) && (*av[ac2] == '-'); ++ac2) {
	switch (*(av[ac2]+1)) {
	case 's':
	    lsrc = 1;
	    break;
	case 'o':
	    ccout = av[ac2]+2;
	    break;
	case 'c':
	    chk = 1;
	    break;
	case 'd':
	    debug = 1;
	    break;
	case 'D':
	    break;
	case 'C':
	    if (av[ac2+1]) set_converter(av[ac2]+2);
	    chk = 1;
	    ccout=0;
	    break;
	case 'I':
	    include_path[include_path_count++] = av[ac2]+2;
	    if (include_path_count<MAX_INCLUDE_PATH_COUNT) 
		break;
	default:
	    error(OPTION);
	    exit(1);
	}
    }
    if (!chk && ccout)
	if ( (freopen(ccout,"w",stdout)) == NULL ) error(FILERR);
    init();
    /* top level */
    while(1) {
	mode=TOP;
	if (gfree > heapsize-30000) {
	    heapsize *= 2;
#if HEAP_REPORT
	    fprintf(stderr,"** heap extended to %d\n",heapsize);
#endif
	    heap = realloc(heap,heapsize*sizeof(int));
	}
	lfree= heapsize;
	codegen_decl_init();
	while (chptrsave!=0) {
	    i = cadr(chptrsave); free_glist2(chptrsave); chptrsave = i;
	}
	while (chsave!=0) {
	    i = cadr(chsave); free_glist2(chsave); chsave = i;
	}
	// a in previous extern f(a) is in current scope, release it
	leave_scope();
	while(getsym(0)==SM) conv->sm_();
	mode=GDECL;
	stmode=0;
	args=0;
	decl();
    }
    /*NOTREACHED*/
}

/*
    error handler
    when EOF, process next file
 */
extern void
error(int n)
{
    if(n == EOFERR) {
	if(filep!=filestack) {
	    fclose(filep->fcb);
	    lineno=filep->ln;
	    --filep;
	    copy_current_file_dir(filep->name0);
	    return;
	} else if(ac2!=ac) {
	    fclose(filep->fcb);
	    newfile();
	    return;
	} else if(mode == TOP) {
	    if (chk) {
		fprintf(stderr, "Total internal labels : %u.\n",labelno-1);
		fprintf(stderr, "Total global variables: %u bytes.\n",gpc);
	    }
	    closing();
	    exit(0);
	}
    }
    if (conv->error_(n)) return;
    fprintf(stderr,"%s:%d:%s\n",filep->name0,lineno,
	(n==FILERR) ? "Can't open specified file" :
	(n==DCERR) ? "Declaration syntax" :
	(n==RDERR) ? "Redefined" :
	(n==STERR) ? "Statement syntax" :
	(n==EXERR) ? "Expression syntax" :
	(n==CNERR) ? "Constant required" :
	(n==CHERR) ? "Illegal character" :
	(n==GSERR) ? "Too many global symbols" :
	(n==HSERR) ? "Too many string or symbols" :
	(n==LSERR) ? "Too many local symbols" :
	(n==MSERR) ? "Too many macro symbols" :
	(n==STRERR) ? "Too many strings or macros" :
	(n==LNERR) ? "Line too long" :
	(n==NMERR) ? "Name too long" :
	(n==MMERR) ? "malloc error" :
	(n==EOFERR) ? "Unexpected end of file" :
	(n==MCERR) ? "Macro syntax" :
	(n==INCERR) ? "Include syntax" :
	(n==HPERR) ? "Too long expression" :
	(n==TYERR) ? "Type mismatch" :
	(n==LVERR) ? "Lvalue required" :
	(n==UDERR) ? "Undeclared identifier" :
	(n==OPTION) ? "Illegal option" :
	(n==RGERR) ? "too many register usage (internal error)" :
	(n==REG_ERR) ? "illegal register var" :
	(n==INERR) ? "bad initialization" :
	(n==CODE_ERR) ? "goto code is necessary" :
	"Bug of compiler");
    errmsg();
    exit(1);
}

static void
errmsg(void)
{
    char *p,*lim;

    if(lineno==0) return;
    fprintf(stderr,"%s",linebuf);
    lim=chptr;
    while (chptrsave) {
	lim = (char*)car(chptrsave);
	chptrsave = cadr(chptrsave);
    }
    for (p=linebuf; p < lim;)
	    fprintf(stderr,(*p++ == '\t') ? "\t" : " ");
    fprintf (stderr,"^\n");
}

/*
     required symbol check
 */
static void
checksym(int s)
{
    char *p;

    if (sym != s) {
	p=(s==RPAR) ? "')'": (s==RBRA) ? "']'": (s==SM) ? "';'":
	  (s==LPAR) ? "'('": (s==WHILE) ? "'while'":
	  (s==ASS) ? "'='": 
	  (s==COLON) ? "':'": "Identifier";
	fprintf(stderr,"%d:%s expected.\n",lineno,p);
	errmsg();
    } else
	getsym(0);
}

/*
    heap array memory pool initialize
    init before reserve()
    this can be called twice or more.
 */

static void
heap_init()
{
    gpc=glineno=0;
    if (!heap) heap = (int *)malloc(heapsize*sizeof(int));
    if (!heap) error(MMERR);
    gfree=ilabel=1;
    labelno=2;
    lfree=heapsize;
}

/*
   Par file initialization.
 */

static void
reinit(void)
{
    int i;

    struct cheap *p;

    global_list = &null_nptr;
    cheap=cheap0;
    for(p=cheap;p;p=p->next) p->ptr=p->first;

    nptr_pool = nptr_pool0;
    for(p=nptr_pool;p;p=p->next) p->ptr=p->first;
    free_nptr_list = 0;
    for(i=0;i<GSYMS;i++) htable[i] = 0;

    heap_init();

    reserve("int",INT);
    reserve("void",VOID);
    reserve("char",CHAR);
    reserve("const",KONST);
    reserve("struct",STRUCT);
    reserve("union",UNION);
    reserve("unsigned",UNSIGNED);
    reserve("signed",SIGNED);
    reserve("static",STATIC);
    reserve("goto",GOTO);
    reserve("return",RETURN);
    reserve("break",BREAK);
    reserve("continue",CONTINUE);
    reserve("if",IF);
    reserve("else",ELSE);
    reserve("for",FOR);
    reserve("do",DO);
    reserve("while",WHILE);
    reserve("switch",SWITCH);
    reserve("case",CASE);
    reserve("default",DEFAULT);
    reserve("typedef",TYPEDEF);
    reserve("sizeof",SIZEOF);
    reserve("long",LONG);
    reserve("short",SHORT);
    reserve("extern",EXTRN);
    reserve("defined",DEFINED);
    reserve("register",REGISTER);
    reserve("code",CODE);
    reserve("environment",ENVIRONMENT);
    reserve("float",FLOAT);
    reserve("double",DOUBLE);
    reserve("inline",INLINE);
    reserve("enum",ENUM);
    reserve("volatile",VOLATILE);
    reserve("__volatile__",VOLATILE);
    reserve("typeof",TYPEOF);
    reserve("__typeof__",TYPEOF);
    reserve("__builtin_alloca",ALLOCA);
    reserve("__label__",LABEL);
#if ASM_CODE
    reserve("asm",ASM);
    reserve("__asm__",ASM);
#endif

    codegen_reinit();
    macro_define("__restrict\n"); 
    macro_define("__micro_c__ 1\n");
#ifdef __APPLE__
    macro_define("__APPLE__ 1\n");
#endif
#ifdef bsd
    macro_define("bsd 1\n");
#endif
    for(i=0;av[i]&&av[i][0]=='-'&&av[i][1]=='D';i++) {
	macro_define(av[i]+2);
    }
    current_scope = 0;
    enter_scope();
}

/*
    one time initialization
 */

static void
init(void)
{
    cheap0 = new_cheap();
    nptr_pool0 = new_cheap();
    codegen_init();
    reinit();
    filep=filestack;
    newfile();
    getch();
}

/*
    keep track current directory
 */

extern void
copy_current_file_dir(char *name)
{
    char *s = name;
    char *d = current_file_dir;
    char *p;
    for(p = d;d<current_file_dir+LBUFSIZE && *s; ) {
	if (*s=='/') p = d+1;
	*d++ = *s++;
    }
    *p = 0;
}

/*
    search possible exisiting file name
      with new extension hoge.c -> hoge.s     
 */

static int first_newfile = 1;

extern char *
make_filename_with_extension(char *filename,char *ext)
{
    char *p=cheap->ptr;
    char *s,*t;
    struct cheap scheap,scheap1;
    save_cheap(&scheap,cheap);

    if (! *filename) filename="mcout";
    for (t=0,s=filename;(*cheap->ptr = *s);cheap=increment_cheap(cheap,&p)) {
        if (*s++ =='.') {
            t=cheap->ptr;
	    save_cheap(&scheap1,cheap);
        }
    }
    if (t) {
	cheap = reset_cheap(&scheap1);
        cheap->ptr = t;
	*cheap->ptr='.';
    } else {
	cheap->ptr[-1]='.';
	cheap->ptr--;
    }
    cheap = increment_cheap(cheap,&p);
    for(s = ext; *s; s++) {
	*cheap->ptr = *s; cheap = increment_cheap(cheap,&p);
    }
    cheap = reset_cheap(&scheap);
    return p;
}

/*
    start new file in argument list
    create filep 
 */

static void
newfile(void)
{
    char *s;

    if (!first_newfile) {
	closing();
	reinit();
    } else
	first_newfile = 0;
    lineno=0;
    if (chk) fprintf(stderr,"%s:\n",av[ac2]);
    if ( (filep->fcb = fopen(av[ac2++],"r")) == NULL ) error(FILERR);
    s = av[ac2-1];
    copy_current_file_dir(s);
    filep->name0 = cheap->ptr;
    filep->inc = 0;
    while((*cheap->ptr = *s++)) cheap = increment_cheap(cheap,&filep->name0);
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,&filep->name0);
    if(!ccout) {
	ccout = make_filename_with_extension(filep->name0,"s");
	if ( (freopen(ccout,"w",stdout)) == NULL ) error(FILERR);
	ccout=0;
    }
    opening(filep->name0);
    conv->open_(filep->name0);

    if (init_src) {
	// before reading any file, perform initialization source
	chinput = init_src;
    }
    getline();
}

static void
set_converter(char *s)
{
    chptr = s;
#if 0
    if (macroeq("c2cbc")) conv=&c2cbc_converter;
    else if (macroeq("cbc2c")) conv=&cbc2c_converter;
    else if (macroeq("c")) conv=&c_converter;
#else
    if (macroeq("c")) conv=&c_converter;
    else conv=&null_converter;
#endif
}

/*
    regist reserved word
 */

static void
reserve(char *s, int d)
{
    NMTBL *nptr;


    (nptr = name_space_search(get_name(s,0,DEF),d?0:MACRO))->sc = RESERVE;
    if (d==0) {
	nptr->sc = MACRO;
	nptr->dsp = (int)""; nptr->ty=0;
    } else {
	nptr->dsp = d;
    }
}

/*
     Parse part
 */

/*
    storage class 
        volatile, static, extern..
 */

static void
storage_class()
{
    if(sym==VOLATILE) {
	getsym(0);
    }
    switch(sym) {
    case STATIC:
	if(mode==LDECL) {
		getsym(0);
		conv->static_();
		mode=STADECL;
		stmode=LDECL;
	} else if(mode==GDECL) {
		getsym(0);
		conv->static_();
		stmode=STATIC;
	} else
	    error(DCERR);
	break;
    case REGISTER:
	if(mode!=LDECL)
	    error(DCERR);
	stmode=REGISTER;
	getsym(0);
	conv->register_();
	break;
    case EXTRN:
	if(mode==LDECL) {
		getsym(0);
		conv->static_();
		mode=GDECL;
		stmode=EXTRN;
	} else if(mode==GDECL) {
	    getsym(0);
	    conv->extern_();
	    stmode=EXTRN;
	} else
	    error(DCERR);
	break;
    case LABEL:  /* GNU extension */
	if(mode==LDECL) {
	    getsym(0);
	    mode = LLDECL;
	} else error(DCERR);
	break;
    case TYPEDEF:
	if(mode==GDECL) {
		getsym(0);
		conv->typedef_();
		mode=GTDECL;
	} else if(mode==LDECL) {
		getsym(0);
		conv->typedef_();
		mode=LTDECL;
	} else
		error(DCERR);
	break;
    }
}

/*
       declaration
         int i;
         int f(arg) {...}
         STORAGE_CLASS TYPESPEC name () { }
         STORAGE_CLASS TYPESPEC name ,
         STORAGE_CLASS TYPESPEC name = value,
 */

static void
decl(void)
{
    NMTBL *n;
    int t,sd;
    if (mode==GDECL) { typedefed=0;  }
    storage_class();
    if((t=typespec())==0) return;
    if(sym==SM) {
	conv->return_type_(t,0,stypedecl);
	conv->sm_(); return;
    }
    type=t;sd=stypedecl;
    n=decl0();
    reverse(t);
    if (n == &null_nptr) {
	/* only bit field allows null identifier */
	if (!(type>0&&car(type)==BIT_FIELD)) {
	    error(DCERR); return;
	}
    }
    if(sym==LC || ( sym!=SM && sym!=COMMA && sym!=ASS)) {
	/* function body */
	if (mode!=GDECL) error(DCERR);
        stypedecl=sd;
	if (car(type)==CODE) {
	    code_decl(n); return;
	} else if (car(type)==FUNCTION) {
	    fdecl(n); return;
	} else error(DCERR);
    } else {
	conv->return_type_(type,n,sd);
	n = def(n);
	if (sym==ASS && n!=&null_nptr) { 
	    decl_data(type,n,0,0); data_closing(n); 
	}
	while(sym==COMMA) {
	    conv->comma_();
	    getsym(0);
	    type=t;
	    n=decl0();
	    reverse(t);
	    if(n == &null_nptr) {
		/* only bitfield allow null field name */
		if (!(type>0&&car(type)==BIT_FIELD))
		    error(DCERR);
	    }
	    conv->return_type_(type,n,1);
	    def(n);
	    if (sym==ASS && n!=&null_nptr) {
		decl_data(type,n,0,0);data_closing(n);
	    }
	}
	if(sym!=SM) error(DCERR);
	conv->sm_();
	if(mode==GTDECL)
	    mode=GDECL;
	if(mode==STADECL||mode==LTDECL)
	    mode=LDECL;
    }
}

/*
      type specification
 */
static int
typespec(void)
{
    int t = INT;
    int slfree;
    int smode,stype;
    stypedecl = 0;

    while (sym==KONST) {
	getsym(0);
    }
    if (sym==INLINE) {
	getsym(0);  // should be static?
    }
    switch(sym) {
    case VOID:
    case INT:
    case CHAR:
    case CODE:
    case FLOAT:
    case DOUBLE:
	t= sym;
	getsym(0);
	break;
    case ENUM:
	t = edecl();
	break;
    case STRUCT:
    case UNION:
	t=sdecl(sym);
	break;
    case SIGNED:
	t = INT;
	if(getsym(0)==INT) getsym(0);
	else if (sym==CHAR) { getsym(0); t = CHAR; }
	else if (sym==SHORT) { 
	    t = SHORT; 
	    if(getsym(0)==INT) getsym(0);
	} else if (sym==LONG) {
	    getsym(0); 
	    t = INT;
	    if(sym==LONG) {
		if(getsym(0)==INT) getsym(0);
		t=LONGLONG;
	    } else if(sym==INT) {
		getsym(0);
		t=INT;
	    }
	}
	break;
    case UNSIGNED:
	t = UNSIGNED;
	if(getsym(0)==INT) getsym(0);
	else if (sym==CHAR) { getsym(0); t = UCHAR; }
	else if (sym==SHORT) { 
	    t = USHORT; 
	    if(getsym(0)==INT) getsym(0);
	} else if (sym==LONG) {
	    getsym(0); 
	    t = UNSIGNED;
	    if(sym==LONG) {
		if(getsym(0)==INT) getsym(0);
		t=ULONGLONG;
	    } else if(sym==INT) {
		getsym(0);
		t=UNSIGNED;
	    }
	}
	break;
    case SHORT:
	t=SHORT;
	if(getsym(0)==INT) getsym(0);
	break;
    case LONG:
	t=INT;
	getsym(0);
	if(sym==LONG) {
	    getsym(0);
	    t=LONGLONG;
	    if (sym==INT) getsym(0);
	    else if (sym==UNSIGNED) { t=ULONGLONG; getsym(0); break; }
	} else if(sym==DOUBLE) {
	    getsym(0);
	    t=DOUBLE;
	} else if(sym==INT) { getsym(0);
	} else if(sym==UNSIGNED) { t=UNSIGNED; getsym(0); }
	break;
    case TYPEOF:
	getsym(0);
	checksym(LPAR);
	slfree=lfree; stype=type;
	smode = mode; mode = LDECL;
	if((t=typespec())==0) {
	    mode = STAT;
	    expr(0); 
	    t = type;
	}
	lfree=slfree; type=stype;
	mode = smode;
	checksym(RPAR);
	return t;
	break;
    default:
	if(sym==IDENT) {
	    if (nptr->sc==TYPE) {
		t=nptr->ty;
		typedefed=glist2((int)nptr,typedefed);
		getsym(0);
		break;
	    } else if(nptr->sc==EMPTY && gnptr->sc==TYPE) {
		getsym(0);
		break;
	    }
	}
	while (sym==KONST) {
	    getsym(0);
	}
	if(mode==LDECL) return 0;
	t= INT;
    }
    while (sym==KONST) {
	getsym(0);
    }
    return t;
}

/*
      indirect *
      type prefix
 */
static struct nametable *
decl0(void)
{
    NMTBL *n;
    if(sym==MUL) {
	getsym(0);
	while (sym==KONST) {
	    getsym(0);
	}
	n=decl0();
	type=list2(POINTER,type);
	return n;
    }
    return decl1();
}

/*
      type postfix
      a() a[] (type)
 */

static NMTBL *
decl1(void)
{
    NMTBL *n;
    int i,array_type,arg;

    if(sym==LPAR) {
	getsym(0);
	n=decl0();
	checksym(RPAR);
    } else if (sym == IDENT||sym==ALLOCA) {
	n=nptr;
	getsym(0);
    } else {
	/* error(DCERR); */
	n= &null_nptr;
    }
    while(1) {
	if(sym==LBRA) {  /* array */
	    if(getsym(0)==RBRA) {
		getsym(0);
		if(mode==ADECL) {
		    type=list2(POINTER,type);
		} else if (mode==GDECL || stmode==EXTRN) {
		    type=list3(ARRAY,type,0);
		} else {
		    error(DCERR);
		}
	    } else {
		array_type=type;
		i=cexpr(expr(1));
		checksym(RBRA);
		type=list3(ARRAY,array_type,i);
	    }
	} else if(sym==LPAR) { /* function or code segment */
	    if(mode==GDECL) {
		mode=ADECL;getsym(0);mode=GDECL; /* ??? */
	    } else
		getsym(0);
	    n->dsp=0;
	    if(stmode==EXTRN) n->sc=EXTRN;
	    else if(stmode==STATIC) n->sc=STATIC;
	    if (type==CODE) {
		n->ty=CODE;
		if(sym==RPAR) {
		    getsym(0);arg=0;
		} else {
		    if (mode==ADECL) {
			enter_scope();
			arg=adecl(n);
			leave_scope();
		    } else 
			arg=adecl(n);
		}
		type=glist3(CODE,CODE,arg);
	    } else {
		if(sym==RPAR) {
		    getsym(0);arg=0;
		} else {
		    if (mode==ADECL) {
			enter_scope();
			arg=adecl(n);
			leave_scope();
		    } else 
			arg=adecl(n);
		}
		type=glist3(FUNCTION,type,arg);
	    }
	    /* Do not set n->ty here. It could be K&R style arguments or
               struct field names */
            /* in GDECL n->dsp contains real parameter, if not,
               it contains arg type list. Real parameter list is compatible
               with arg type list. See def/ADECL  */
	    if (mode!=GDECL)
		n->dsp=arg;
	} else if(sym==COLON) { /* bit-field */
	    if (mode==GSDECL||mode==GUDECL||mode==LSDECL||mode==LUDECL) {
		if (scalar(type) || type==LONGLONG || type==ULONGLONG) {
		    getsym(0);
		    type = list3(BIT_FIELD,type,
			list3(type /*store type*/,0 /*bit offset*/,symval));
		    getsym(0);
		}
	    } else
		error(DCERR);
	    return n;
	} else
	    return n;
    }
    /* NOT REACHED */
}

/*
     argument declaration (ANSI)
	    argtypes=list2(type,argtypes);
 */

static int
adecl(NMTBL *n)
{
    NMTBL *arg,*sfnptr;
    int t;
    int stype,smode,sd,sargs,sstmode;
    int argtypes;

    sstmode=stmode; stmode=REGISTER; /* nobody use this? */
    stype=type;
    sfnptr=fnptr;
    fnptr=n;
    sd = sdecl_f;
    sdecl_f = 0;
    argtypes = 0;
    smode = mode;
    mode=ADECL;
    args = 0;
    n->dsp=0;
    for(;;) {
	if(sym==IDENT && nptr->sc!=TYPE) {
	    type=INT;  /* naked argument, old K&R C */
	    def(nptr);
	    getsym(0);
	    if(sym==RPAR) break;
	} else {
	    if(sym==DOTS) {
		argtypes=list2(DOTS,argtypes);
		getsym(0);
		break;
	    }
	    if((t=typespec())==0) {
		error(DCERR);
		break;
	    }
	    type=t;
	    if(sym!=COMMA && sym!=RPAR) {
		sargs = args;
		arg=decl0();
		args = sargs;
		reverse(t);
		if (arg != &null_nptr) {
		    if (smode==GDECL)
			def(arg);
		}
	    }
	    argtypes=list2(type,argtypes);
	    if(sym==RPAR) break;
	}
	if (sym!=COMMA) error(DCERR);
	getsym(0);
    }
    argtypes=reverse0(argtypes);
    n->dsp=reverse0(n->dsp);
    checksym(RPAR);
    mode=smode;
    fnptr=sfnptr;
    type=stype;
    sdecl_f = sd;
    stmode=sstmode;
    return argtypes;
}

/* reverse modifies type also */

static void
reverse(int t1)
{
    int t2,t3;
    t2=t1;

    while(type!=t1) {
	t3=cadr(type);
	cadr(type) = t2;
	t2=type;
	type=t3;
    }
    type = t2;
}

/*
    destructive reverse
 */
int
reverse0(int t1)
{
    int t2,t3;

    t2=0;
    while(t1) {
	t3=cadr(t1);
	cadr(t1) = t2;
	t2=t1;
	t1=t3;
    }
    return t2;
}

/*
     calcurate size of type
 */

extern int
size(int t)
{
    if (t<0) {
	switch(t) {
	case CHAR: return 1;
	case UCHAR: return 1;
	case VOID: return 1;  /* not 0 */
	case SHORT: return size_of_short;
	case USHORT: return size_of_short;
	case REGISTER: return size_of_int;
	case DREGISTER: return size_of_double;
	case FREGISTER: return size_of_float;
	case LREGISTER: return size_of_longlong;
	case FLOAT: return size_of_float;
	case DOUBLE: return size_of_double;
	case LONGLONG: return size_of_longlong;
	case ULONGLONG: return size_of_longlong;
	case ENUM: return size_of_int;
	default:
	    if(scalar(t)) return size_of_int;
	    error(DCERR);
	}
    } 
    /* type represented in a list */
    switch(car(t)) {
    case STRUCT:
    case UNION:
	if(cadr(t)==-1) error(DCERR);
	return(cadr(t));
    case ARRAY:
	return(size(cadr(t))*caddr(t));
    case CODE:
	return size_of_int;
    case FUNCTION:
	return size_of_int;
    case POINTER:
	return size_of_int;
    default:
	error(DCERR);
    }
    return 0;
}

#define hash_value(hash,ch) (hash = (39*hash)^((unsigned char)(ch)))

/*
    new name for static global variable
         delimitor _
 */

extern NMTBL *
new_static_name(char *name,int delimit)
{

    int ndsp,ch,len=0;
    char *p = cheap->ptr;
    char *q = name;
    unsigned int hash = 0;
    struct cheap scheap;
    NMTBL *n;
#if 1
    NMTBL *nlist;
#endif

    while((ch = *name++)) {
	hash_value(hash,*cheap->ptr = ch);
	increment_cheap(cheap,&p);len++;
    }
    ndsp = ++stat_no;
    *cheap->ptr = delimit;
    increment_cheap(cheap,&p); if (delimit=='_') len++;
    while(ndsp>0) {
	ch = ndsp%10+'0';
	if (delimit=='_')
	    hash_value(hash,*cheap->ptr = ch);
	else
	    *cheap->ptr = ch;
	increment_cheap(cheap,&p);if (delimit=='_') len++;
	ndsp /= 10;
    }
    *cheap->ptr = 0;
    increment_cheap(cheap,&p);
    save_cheap(&scheap,cheap);
    n = name_space_search(nlist=hash_search(delimit=='_'?p:q,&scheap,len,hash,DEF),0);
    n->nm = p;
    return n;
}

/*
    data declaration
    a[] = {,,,,};
 */

#define LOCAL_STRUCT_INIT_STATIC 1

static void
decl_data_field(int type,NMTBL *n,int offset)
{
    int e,t1;
    int foffset;
    int offset0 = offset;
    int decl_str_init_save = decl_str_init;
    int mode_save=mode;
    NMTBL *nptr0;

    decl_str_init = 0;
    if(cadr(type)==-1) {
	error(DCERR);
	return;
    }
    if (mode==LDECL && LOCAL_STRUCT_INIT_STATIC) {
	// uninitialized part should be 0.
	// local var init cannot postponed because of assign_expr0/type
	nptr0=new_static_name("__lstruct",'_');
	nptr0->next = local_static_list; local_static_list = nptr0;
	nptr0->sc = GVAR;
	e = size(type);
	nptr0->ty = type;
	mode=STADECL;
	decl_data_field(type,nptr0,offset);
	init_vars = list2(
	    list4(STASS,list2(LVAR,n->dsp+offset),
		list3(RSTRUCT,list3(GVAR,0,(int)nptr0),e),e),
	    init_vars);
	return;
    }
    mode=SFDINIT;
    t1 = caddr(type);  /* list of fields */
    while(1) {
	getsym(0);
	if (sym==PERIOD) { /* struct/union field initializaer */
	    getsym(0);
	    if (sym==IDENT) {
		t1 = search_struct_type(type,nptr->nm,&foffset);
		getsym(0);
		if (sym==ASS) {
		    decl_data(t1,n,foffset,0);
		} else
		    error(TYERR); /* should be initialization error */
	    } else
		error(TYERR); /* should be initialization error */
	} else {
	    if(!t1) {
		// empty field case (it can happen...)
		break;
	    }
	    // next decl_data must skip getsym
	    offset = decl_data(car(t1),n,offset,1);  /* alignment? */
	    t1 = cadr(t1);
	}
	if ( t1 && sym==COMMA) { conv->comma_(); continue; }
	// if (!t1 && sym==COMMA) getsym(0); /* extra comma */
	if (sym==RC) break; // premature end
    }
    mode = mode_save;
    offset = offset0;
    /*
	 decl_str_init
	 list4(offset,next,expression,type);
     */
    while (decl_str_init) {
	offset= car(decl_str_init);
	e=caddr(decl_str_init);
	type=cadddr(decl_str_init);
	if (offset!=offset0) {
	    // make space
	    assign_data(list2(CONST,offset-offset0),EMPTY,n,offset0);
	}
 	offset0 = assign_data(e,type,n,offset);
	decl_str_init = cadr(decl_str_init);
    }
    decl_str_init = decl_str_init_save;
}

/*
    data structure initialization
 */

static int
decl_data(int t, NMTBL *n,int offset,int skip)
{
    int t1,e,i,mode_save;

    if (offset==0 && n->sc==GVAR && n->dsp==-1) {
	/* duplicate initialization */
	error(INERR);
    }
    conv->decl_data_();
    mode_save = mode;
    mode=STAT;
    if (!skip) getsym(0);
    if (sym==RC) {  /* premature end (not necessary?) */
	conv->decl_data_end_();
	mode = mode_save;
	return offset;
    } else if (scalar(t)) {
 	e=expr1();
	mode = mode_save;
 	if(car(e)!=CONST && t==CHAR)
 	    error(TYERR);
 	offset = assign_data(e,t,n,offset);
 	type=t;
	return offset;
    } else if (t==FLOAT||t==DOUBLE||t==LONGLONG||t==ULONGLONG) {
 	e=expr1();
	mode = mode_save;
 	offset = assign_data(e,t,n,offset);
 	type=t;
	return offset;
    } else if ((t1 = car(t)) && t1==ARRAY) {
	if (sym==LC) {
	    conv->decl_data_begin_();
	    mode = mode_save;
	    t1 = cadr(t);
	    for(i=0;;i++) {
		if (sym!=RC)
		    offset=decl_data(t1,n,offset,0); /* array of some thing */
		if (sym==COMMA) {
		    conv->comma_();
		    continue;
		} else if (sym==RC) {
		    conv->decl_data_end_();
		    if (caddr(t)==0) {           /* size not defined      */
			caddr(t)=i+1;           /* define array size     */
		    } else if (0 && caddr(t)!=i+1) {  /* size match?           */
			error(TYERR);
		    }
		    getsym(0);
		    return offset;
		}
	    }
	    /* NOT REACHED */
	} else if (cadr(t)==CHAR) {
	    e=expr1();
	    mode = mode_save;
	    if(car(e)!=STRING)
		error(TYERR);
	    offset=assign_data(e,list3(ARRAY,CHAR,size(type)),n,offset);
	    if (caddr(t)==0) {                  /* size not defined      */
		caddr(t)=size(type);           /* define array size     */
	    } else if (caddr(t)!=size(type)) {  /* size match?           */
		error(TYERR);
	    }
	    return offset; /* not reached */
	}
    } else if (t1==BIT_FIELD) {
 	e=expr1();
	mode = mode_save;
 	offset = assign_data(e,t,n,offset);
 	type=t;
	return offset;
    } else if (t1==STRUCT) {
        if (sym==LC) {
            conv->lc_(); conv->decl_data_begin_();
            mode = mode_save;
	    decl_data_field(t,n,offset);
            conv->decl_data_end_(); conv->rc_();
            checksym(RC);
            return offset+size(t);
	} else if (sym==RC) { /* empty case */
	    conv->lc_();
	    return offset;
	} 
    } 
    mode = mode_save;
    error(TYERR); /* should be initialization error */
    return offset; /* not reached */
}

/*
    declaration in struct/union
 */
static void
sdecl_field()
{
    while (getsym(0) != RC) {
        decl();
    }
    if (sdecl_f) conv->rc_();
    getsym(0);
    fields =  reverse0(fields);
}

#if 0
static void
print_fields(int fields,char *s) {
    for(;fields;fields=cadr(fields)) {
	fprintf(stderr,"%s %s %d %d\n",s,(char*)caddr(fields),car(fields),cadddr(fields));
    }
    fprintf(stderr,"\n");
}
#endif

/* 
   struct/union
       tag  ... struct/union name
	    nptr0->sc = TAG;
	    nptr0->ty = list4(...)
       type ...  list4(STRUCT,disp,fields,(int)nptr0);
       filed   ... assoc list defined in def();
 */
static int
sdecl(int s)
{
    int smode,sdisp,sbit_field_disp,type0=0;
    NMTBL *nptr0,*gnptr0;
    int sfields = fields;

    fields = 0;
    smode=mode;
    if (mode==GDECL || mode==GSDECL || mode==GUDECL || mode==GTDECL)
	mode=(s==STRUCT?GSDECL:GUDECL);
    else
	mode=(s==STRUCT?LSDECL:LUDECL);
    sdisp=disp;
    sbit_field_disp=bit_field_disp;
    disp=0;
    bit_field_disp=0;
    if (sdecl_f) conv->sdecl_(s);
    if (getsym(TAG) == IDENT) {
	nptr0 = nptr;
	gnptr0 = gnptr;
	if (sdecl_f) conv->id_(sym,nptr);
	if (getsym(0) == LC) {
	    if (sdecl_f) conv->lc_();
	    if(nptr0->sc == EMPTY) nptr0=gnptr0;
	    if (nptr0->sc!=TAG && nptr0->sc != EMPTY) error(DCERR);
	    nptr0->sc = TAG;
	    nptr0->ty = list4(s,-1,0,(int)nptr0);
	    sdecl_field();
	    caddr(nptr0->ty)=fields;
	    cadr((type0 = nptr0->ty))=disp;
	    /* type0 = list4(s,disp,fields,0); now ... */
	} else {
	    /* struct tag name */
	    if(nptr0->sc == EMPTY) nptr0=gnptr0;
	    if(nptr0->sc == EMPTY) nptr0->sc = TAG;
	    if(nptr0->sc != TAG) error(TYERR);
	    if (nptr0->ty) {
		fields = caddr(nptr0->ty);
		disp = cadr(nptr0->ty);
	    }
	    conv->comment_(' ');
	    type0 = list4(s,disp,fields,(int)nptr0);
	}
    } else if(sym==LC) {
	if (sdecl_f) conv->lc_();
	sdecl_field();
	type0 = list4(s,disp,fields,0);
    }
    else error(DCERR);

    stypedecl=1;
    disp=sdisp;
    bit_field_disp=sbit_field_disp;
    mode=smode;
    fields = sfields;
    return type0;
}

/*
    enum
 */

static int
edecl()
{
    int smode=mode;
    int sdisp=disp;
    NMTBL *nptr0;

    if (mode==GDECL || mode==GTDECL)
	mode=GEDECL;
    else
	mode=LEDECL;
    if (getsym(0) == IDENT) {
	nptr->sc = TAG;
	getsym(0);
    }
    if(sym==LC) {
	while (getsym(0) == IDENT) {
	    nptr->sc = ENUM;
	    nptr->ty = INT;
	    nptr0 = nptr;
	    if (getsym(0) == ASS) {
		getsym(0);
		disp = cexpr(expr1());
	    }
	    nptr0->dsp = disp;
	    if (sym!=COMMA) break;
	    disp++;
	}
	checksym(RC);
    }
    type = ENUM;
    disp=sdisp;
    mode=smode;
    return type;
}

/* code sgement
     simpler than fdecl, because it does not have return value.
 */
static void
code_decl(NMTBL *n)
{
    int t,arglist;

    if(!chk) gen_code_enter(n->nm);
    extrn_use(n);
    local_static_list = &null_nptr;
    fnptr=n;
    n->sc = CODE;
    n->ty = type;
    fcheck(n);
    disp = -args;
    mode=ADECL;
    if (sym!=LC) {
	arglist=fnptr->dsp;
	args=fnptr->dsp=0;
	while (sym!=LC) { /* argument declaration !ANSI */
	    decl(); getsym(0);
	}
	disp = -args;
	fnptr->dsp = arg_reorder(arglist,fnptr->dsp);
	// fnptr->dsp = reverse0(fnptr->dsp);
    }
    /* reverse all argument offset (with size) */
    arglist = fnptr->dsp;
    for(t=arglist;t;t=cadr(t)) {
	n=(NMTBL *)caddr(t);
	if(n->sc==LVAR)
	    n->dsp = -n->dsp-cadddr(t);
    }
    arg_register(fnptr);
    conv->code_(fnptr);
    typedefed=0;
    /* local variable declaration */
    stmode=0;
    mode=STAT;
    init_vars=0;
    while (typeid(getsym(0)) || sym==STATIC || sym==EXTRN || sym==TYPEDEF) {
	mode=LDECL;
	decl();
	mode=STAT;
    }
    conv->localvar_end_();
    control=1;
    cslabel = -1;
    if(!chk) gen_code_enter1(args);
    emit_init_vars();
    while(sym!=RC) statement(0);
    if(control)
	error(STERR);
    control=0;
    conv->code_end_();
    if(!chk) gen_code_leave(fnptr->nm);
    args = 0;
}

static NMTBL *tmp_struct;

/* local decl can be used, after {}         */
/*  but it's lexical scope remains after {} */
/*  my be in for(int i=....) not yet  (fixed already?)      */

static void
local_decl()
{
    enter_scope();
    init_vars=0;
    /* local variable declaration */
    stmode=0;
    mode=STAT;
    while (typeid(getsym(0)) || sym==STATIC || sym==EXTRN || sym==LABEL
		|| sym==REGISTER || sym==TYPEDEF) {
	mode=LDECL;
	stmode=0;
	decl();
	mode=STAT;
    }
    conv->localvar_end_();
}

/* function define */


static void
fdecl(NMTBL *n)
{
    int sd = stypedecl;
    int arglist;
    if(!chk) gen_enter(n->nm);
    extrn_use(n);
    local_static_list = &null_nptr;
    fnptr=n;
    retlabel=fwdlabel();
    retcont = 0;
    if (tmp_struct) { tmp_struct->sc = 0; tmp_struct->nm = 0; }
    tmp_struct = 0; /* a = f().filed  */

    n->ty = type;
    fcheck(n);
    n->sc = FUNCTION;
    mode=ADECL;
    if (sym!=LC) {
	arglist = fnptr->dsp;
	fnptr->dsp =args=0; 
	while (sym!=LC) { /* argument declaration !ANSI */
	    stmode=0;
	    decl(); getsym(0);
	}
	// This order can be different from proto type. Proto type is correct.
        // Recalculate offset using prototype list.
	// arglist is set by adecl() and is reversed.
	fnptr->dsp = arg_reorder(arglist,fnptr->dsp);
    }
    fnptr->dsp=reverse0(fnptr->dsp);
    fdecl_struct(fnptr->ty); /* insert extra argument for struct passing */
    disp=0;
    arg_register(fnptr);
    typedefed=0;
    conv->function_(fnptr,sd); conv->lc_();
    init_vars=0;

    /* local variable declaration */
    local_decl();
    control=1;
    cslabel = -1;
    if(!chk) gen_enter1();
    emit_init_vars();
    while(sym!=RC) statement(0);
    leave_scope();

    conv->function_end_(); conv->rc_();
    if(!chk) gen_leave(control,n->nm);
    retpending = 0;
    control=0;
    arglist=0;
}

/*
    basic C statement
 */

static void
statement(int use)
{
    int slfree;

    if(sym==SM) {
	conv->sm_();
	getsym(0); return;
    }
    switch(sym) {
    case IF:
	doif();
	return;
    case WHILE:
	dowhile();
	return;
    case DO:
	dodo();
	return;
    case FOR:
	dofor();
	return;
    case SWITCH:
	doswitch();
	return;
    case LC:
	docomp(use);
	return;
    case BREAK:
	checkret();
	conv->break_();
	if (control) gen_jmp(blabel);
	getsym(0);
	checksym(SM);
	return;
    case CONTINUE:
	checkret();
	conv->continue_();
	if (control) gen_jmp(clabel);
	getsym(0);
	checksym(SM);
	return;
    case CASE:
	docase();
	statement(use); return;
    case DEFAULT:
	dodefault();
	statement(use); return;
    case RETURN:
	doreturn();
	return;
    case GOTO:
	dogoto();
	return;
#if ASM_CODE
    case ASM:
	doasm();
	return;
#endif
    default:
	checkret();
	if(sym==IDENT&&skipspc()==':') {
	    dolabel();
	    statement(use);
	} else {
	    if (use) {
		lastexp = expr(0);
		return;
	    } else {
		slfree=lfree;
		gexpr(expr(0),use);
		lfree=slfree;
		conv->sm_();
		checksym(SM);
	    }
	}
    }
}

static void
doif(void)
{
    int l1,l2,slfree;
    getsym(0);
    checksym(LPAR);
    conv->if_();
    slfree=lfree;
    checkret();
    l1 = bexpr(expr(0),0,fwdlabel());
    lfree=slfree;
    conv->if_then_();
    checksym(RPAR);
    statement(0);
    checkret();
    if(sym==ELSE) {
	conv->if_else_();
	if ((l2 = control))
	    gen_jmp(l2=fwdlabel());
	fwddef(l1);
	getsym(0);
	statement(0);
	checkret();
	if (l2) fwddef(l2);
    }
    else fwddef(l1);
    conv->if_endif_();
}

static void
dowhile(void)
{
    int sbreak,scontinue,slfree,e;

    sbreak=blabel;
    scontinue=clabel;
    blabel=fwdlabel();
    control=1;
    checkret();
    clabel=backdef();
    conv->while_();
    getsym(0);
    checksym(LPAR);
    slfree=lfree;
    e=expr(0);
    checksym(RPAR);
    conv->while_body_();
    if(sym==SM) {
	bexpr(e,1,clabel);
	lfree=slfree;
	conv->sm_();
	getsym(0);
    } else {
	bexpr(e,0,blabel);
	// lfree=slfree;
	statement(0);
	checkret();
	if(control)
	    gen_jmp(clabel);
    }
    conv->while_end_();
    fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

static void
dodo(void)
{
    int sbreak,scontinue,l,slfree;

    sbreak=blabel;
    scontinue=clabel;
    blabel=fwdlabel();
    clabel=fwdlabel();
    control=1;
    checkret();
    l=backdef();
    conv->dowhile_();
    getsym(0);
    statement(0);
    checkret();
    fwddef(clabel);
    checksym(WHILE);
    checksym(LPAR);
    slfree=lfree;
    conv->dowhile_cond_();
    bexpr(expr(0),1,l);
    lfree=slfree;
    checksym(RPAR);
    conv->dowhile_end_();
    checksym(SM);
    fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

static void
dofor(void)
{
    int sbreak,scontinue,l,e,slfree,dflag=0;

    sbreak=blabel;
    scontinue=clabel;
    blabel=fwdlabel();
    conv->for_();
    getsym(0);
    checksym(LPAR);
    slfree=lfree;
    if (typeid(sym) || sym==REGISTER ) {
	enter_scope(); dflag = 1;
	mode=LDECL;
	stmode=0;
	decl();
	mode=STAT;
	checkret();
	emit_init_vars();
	getsym(0);
    } else if(sym!=SM) {
	checkret();
	gexpr(expr(0),0);
	checksym(SM);
	conv->for1_();
    } else {
	conv->for1_();
	getsym(0);
    }
    lfree=slfree;
    control=1;
    checkret();
    l=backdef();
    if(sym!=SM) {
	bexpr(expr(0),0,blabel);
	checksym(SM);
	conv->for2_();
    } else {
	conv->for2_();
	getsym(0);
    }
    lfree=slfree;
    if(sym==RPAR) {
	clabel=l;
	conv->for_body_();
	getsym(0);
	statement(0);
	checkret();
    } else {
	clabel=fwdlabel();
	e=expr(0);
	conv->for_body_();
	checksym(RPAR);
	statement(0);
	checkret();
	fwddef(clabel);
	gexpr(e,0);
	lfree=slfree;
    }
    if (dflag) leave_scope();
    conv->for_end_();
    gen_jmp(l);
    fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

/*
    compound statement {}
 */
static void
docomp(int use)
{
    conv->lc_();
    local_decl();
    emit_init_vars();
    while(sym!=RC) statement(use); 
    conv->rc_();
    leave_scope();
    getsym(0);
}

/*
     CASE_CODE generates table jump
 */

static void
doswitch(void)
{
    int sbreak,scase,sdefault,slfree,svalue,slist;

    checkret();
    slist = cslist;
    cslist = 0;
    sbreak=blabel;      /* save parents break label */
    blabel=fwdlabel();
    sdefault=dlabel;    /* save parents default label */
    dlabel=0;
    scase=cslabel;      /* save parents next case label */
    conv->switch_();
    getsym(0);
    checksym(LPAR);
    slfree=lfree;
    svalue=csvalue1;      /* save parents switch value */
    gexpr(expr(0),1);
    if (!scalar(type)) error(EXERR);
    csvalue1=csvalue() ;
    lfree=slfree;
    checksym(RPAR);
    conv->switch_body_();
    cslabel = control = 0;
    /* next syntax should be a case statement but...  
	main() {
	    int i=3,j=1,k=0;
	    switch(i)  {
		for(;j<10;j++) {
		    case 3: k++; case 2: k++; case 1: k++; case 0: k++;
		}
	    }
	    printf("%d\n",k);
	}
       In this case, we have to jump into the first case label.
       Can be done in checkret();
    */
    statement(0);
    conv->switch_end_();
    checkret();
#if CASE_CODE
    if (control) gen_jmp(blabel);
    genswitch(cslist,cslabel);
#else
    if(dlabel) def_label(cslabel,dlabel);
    else fwddef(cslabel);
#endif
    csvalue1=svalue;
    cslabel=scase;
    dlabel=sdefault;
    fwddef(blabel);
    blabel=sbreak;
    cslist = slist;
}

/* used in insert ascend */
static int
docase_eq()
{
    error(-1);  // duplicate case value
    return 0;   // remove duplicate value
}

static void
docase(void)
{
#if CASE_CODE
    int l,clist=0,c;
    l = fwdlabel();
    while(sym==CASE) {
	conv->case_begin_(0,0);
	getsym(0);
	clist=glist3(cexpr(expr(1)),clist,l);
	conv->case_(0,0);
	checksym(COLON);
    }
    if (retpending) { ret(); retpending=0; }
    if (!cslabel) {
	if (!control) {
	    // immediate after switch(i) (usual case)
	    // use it for jump to table lookup

	    cmpdimm(car(clist),csvalue1,cslabel=fwdlabel(),1);

	    // Insert anyway to check duplicate case value.
	    // Mark it already used.

	    caddr(clist)=0;

	} else {
	    // checkret() sequence inconsistent
	    // This can't happen, because checkret() force table lookup jump
	    // before any executable instruction in switch such as switch-for.
	    error(-1);  
	}
    }
    // Make ascend order list of case value
    while(clist) {
	clist = cadr(c=clist); cadr(c) = 0;  // insert destroy cadr of clist
	cslist=insert_ascend(cslist,c,docase_eq);
    }
    fwddef(l);
    control=1;
#else
    /* casading branch implementation */
    int c,l,slfree;
    l = 0;
    if (retpending) ret();
    slfree=lfree;
    c=0;
    while(sym==CASE) {
	conv->case_begin_(c,0);
	getsym(0);
	c=list2(cexpr(expr(1)),c);
	conv->case_(c,0);
	checksym(COLON);
    }
    if (control) {
	control=0;
	gen_jmp(l=fwdlabel());
    }
    if (cslabel) fwddef(cslabel);
    while(cadr(c)) {
	cmpdimm(car(c),csvalue1,l,0);
	c=cadr(c);
    }
    cmpdimm(car(c),csvalue1,cslabel=fwdlabel(),1);
    if (l) fwddef(l);
    lfree=slfree;
#endif
}

static void
dodefault(void)
{
    control=1;
    checkret();
    getsym(0);
    checksym(COLON);
    if (dlabel) error(STERR);  // double default:
    dlabel = backdef();
    conv->case_(0,1);
}

static void
doreturn(void)
{
    int slfree,e,e1;

    if (!cslabel) gen_jmp(cslabel = fwdlabel());
    if(getsym(0)==SM) {
	// should check fnptr have no return value
	conv->return_();
	conv->return_end_();
	getsym(0);
	retpending = 1;
	return;
    }
    conv->return_();
    slfree=lfree;
    if (struct_return) {
	e = expr(0);
	if ((car(type)==STRUCT || car(type)==UNION)&&
		size(type)==cadr(struct_return)) {
	    if(car(e)==RSTRUCT && car(cadr(e))==FUNCTION) {
                /* pass the return pointer to the called function */
		replace_return_struct(cadr(e),
		    rvalue_t(car(struct_return),caddr(struct_return)));
		gexpr(cadr(e),0);
	    } else {
		type = caddr(struct_return);
		// e1 = rvalue_t(cadr(struct_return),INT); /* size */
		e1 = cadr(struct_return); /* size */
		gexpr(list4(STASS,rvalue(car(struct_return)),e,e1),0);
	    }
	} else {
	    error(TYERR); /* should check compatible */
	}
    } else {
	gexpr(correct_type(expr(0),cadr(fnptr->ty)),1);
    }
    lfree=slfree;
    conv->return_end_();
    checksym(SM);
    /* control = 0; still control continue until pending return emission */
    retpending = 1;
}

static void
dogoto(void)
{
    NMTBL *nptr0;
    int t,e1,e2,env;

    checkret();
    conv->goto_();
    getsym(0);
    e1 = expr(0);
    t=car(e1);
    if (type==VOID) {
	if (car(e1)==RINDIRECT) {
	    gen_indirect_goto(cadr(e1));
	} else error(TYERR);
	checksym(SM);
	return;
    }
    if (t==FNAME) {
	nptr0 = (NMTBL *)cadr(e1);
	t = nptr0->sc;
	if (t==EMPTY||t==EXTRN1||t==EXTRN) {
	    nptr0->sc=EMPTY;
	    nptr0=l_top_search(nptr0->nm,0);
	    nptr0->sc = FLABEL;
	    gen_jmp(nptr0->dsp = fwdlabel());
	} else if (t==FLABEL||t==BLABEL) {
	    gen_jmp(nptr0->dsp);
	}
	control=0;
	conv->sm_();
	checksym(SM);
	conv->goto_label_(nptr0);
	return;
    }
    if (t==COMMA) {
	env = caddr(e1);
	e1  = cadr(e1);
	t   = car(e1);
    } else {
	env = 0;
    }
    if (t==FUNCTION) {
        /*   CbC continuation */
	conv->jump_(env);
	e2 = cadr(e1);
	if (car(e2) == FNAME) {
	    nptr0=(NMTBL *)cadr(e2);
	    if (nptr0->sc==EMPTY)
		nptr0->sc = EXTRN1;
	    else if(nptr0->sc==FUNCTION)
		nptr0->sc = CODE;
	    if (nptr0->ty>0&&car(nptr0->ty)==FUNCTION)
		car(nptr0->ty)=CODE;
	}
	gexpr(list3(CODE,e1,env),0);
	control=0;
	conv->sm_();
	checksym(SM);
	return;
    }
    error(STERR);
    return;
}

static void
dolabel(void)
{
    NMTBL *nptr1;
    control=1;
    checkret();
    if(nptr->sc == FLABEL)
	fwddef(nptr->dsp);
    else if(nptr->sc != EMPTY && nptr->sc != EXTRN1)
	error(TYERR);
    nptr->sc=EMPTY;
    nptr1=l_top_search(nptr->nm,0);
    nptr1->sc = BLABEL;
    nptr1->dsp = backdef();
    conv->label_();
    getsym(0);
    checksym(COLON);
}

#if ASM_CODE

/*
     asm( asm_string : output expr : input expr : option_string )
 */
static void
doasm()
{
    int e1 = 0, asm0 = 0, input = 0, out = 0, opt = 0;
    int e;

    checkret();
    getsym(0);
    if (sym==VOLATILE) getsym(0);
    checksym(LPAR);
    // asm string
    if (sym!=STRING) error(DCERR);
    asm0=list3(STRING,(int)nptr->nm,nptr->dsp);
    getsym(0);
    if (sym!=COLON) error(DCERR);
    do {
	// output expression
	getsym(0);
	if (sym==COLON) break;
	if (sym!=STRING) error(DCERR);
	out=list2(list3(STRING,(int)nptr->nm,nptr->dsp),out);
	getsym(0);
	e1=list2(e=expr1(),e1);
	lcheck(e);
    } while(sym==COMMA);
    if (sym==COLON) {
	do {
	    // input expression
	    getsym(0);
	    if (sym==COLON) break;
	    if (sym!=STRING) error(DCERR);
	    input=list2(list3(STRING,(int)nptr->nm,nptr->dsp),input);
	    getsym(0);
	    e1=list2(expr1(),e1);
	} while(sym==COMMA);
    }
    if (sym==COLON) {
	do {
	    // option string
	    getsym(0);
	    if (sym!=STRING) error(DCERR);
	    opt=list2(list3(STRING,(int)nptr->nm,nptr->dsp),opt);
	    getsym(0);
	} while(sym==COMMA);
    }
    checksym(RPAR);
    gexpr(list3(ASM,list4(asm0,input,out,opt),e1),0);
    checksym(SM);
}
#endif


/* C expression */

extern int
expr(int noconv)
{
    int r;
    conv->noconv_(noconv);
    r=rvalue(expr0());
    return r;
}

static int
expr0(void)
{
    int e;

    e=expr1();
    while(sym==COMMA) {
	conv->op_(sym);
	getsym(0);e=list3(COMMA,e,rvalue(expr1()));
    }
    return e;
}

static int
expr1(void)
{
    int e1,e2,t,op,no_float;
    e1=expr2();
    no_float = 0;
    switch (sym) {
    case ASS:
	conv->op_(sym);
	lcheck(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr1());
	e1 =  assign_expr(e1,e2,t);
	return e1;
    case RSHIFT+AS: case LSHIFT+AS: case BAND+AS: 
    case EOR+AS: case BOR+AS: case MOD+AS:
	no_float = 1;
    case ADD+AS: case SUB+AS: case MUL+AS: case DIV+AS: 
	conv->op_(sym);
	op = sym-AS;
	lcheck(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr1());
	return assop(e1,e2,op,t,no_float);
    default:
	return(e1);
    }
}

static int
expr2(void)
{
    int e1,e2,e3,t;

    e1=expr3();
    if(sym==COND) {  // e1?e2:e3
	conv->cond_();
	e1=rvalue(e1);
	getsym(0);
	if (sym==COLON) {
	    e2 = 0; // dumb extension of gcc
	    t=type;
	    getsym(0);
	} else {
	    conv->cond1_();
	    e2=rvalue(expr0());
	    t=type;
	    conv->cond2_();
	    checksym(COLON);
	}
	e3=rvalue(expr2());
	conv->cond_end_();
	return cond(t,e1,e2,e3);
    }
    return(e1);
}

static int
expr3(void)
{
    int e,e1;

    e=expr4();
    while(sym==LOR) {  /* || */
	conv->op_(sym);
	e=rvalue(e);
	getsym(0);
	e1=rvalue(expr4());
	if(car(e)==CONST)       e =  cadr(e )?e:e1;
	else if(car(e1)==CONST) e =  cadr(e1)?e1:e;
	else e=list3(LOR,e,e1);
	type = INT;
    }
    return(e);
}

static int
expr4(void)
{
    int e,e1;

    e=expr5();
    while(sym==LAND) { /* && */
	conv->op_(sym);
	e=rvalue(e);
	getsym(0);
	e1=rvalue(expr5());
	if(car(e)==CONST)       e =  cadr(e )?e1:e;
	else if(car(e1)==CONST) e =  cadr(e1)?e:e1;
	else e=list3(LAND,e,e1);
	type = INT;
    }
    return(e);
}

static int
expr5(void)
{
    int e1,e2,t;

    e1=expr6();
    while(sym==BOR) { /* | */
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr6());
	e1=binop(BOR,e1,e2,t,type);
    }
    return(e1);
}

static int
expr6(void)
{
    int e1,e2,t;

    e1=expr7();
    while(sym==EOR) { /* ^ */
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr7());
	e1=binop(EOR,e1,e2,t,type);
    }
    return(e1);
}

static int
expr7(void)
{
    int e1,e2,t;

    e1=expr8();
    while(sym==BAND) { /* & */
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr8());
	e1=binop(BAND,e1,e2,t,type);
    }
    return(e1);
}

static int
expr8(void)
{
    int e1,e2,op,t;

    e1=expr9();
    while((op=sym)==EQ||op==NEQ) {
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr9());
	e1=binop(op,e1,e2,t,type);
	type= INT;
    }
    return e1;
}

static int
expr9(void)
{
    int e1,e2,t,op;

    e1=expr10();
    while((op=sym)==GT||op==GE||op==LT||op==LE) {
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr10());
	e1=binop(op,e1,e2,t,type);
	type= INT;
    }
    return e1;
}

static int
expr10(void)
{
    int e1,e2,t,op;

    e1=expr11();
    while((op=sym)==RSHIFT||op==LSHIFT) {
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr11());
	e1=binop(op,e1,e2,t,type);
    }
    return e1;
}

static int
expr11(void)
{
    int e1,e2,t,op;

    e1=expr12();
    while((op=sym)==ADD||op==SUB) {
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr12());
	e1=binop(op,e1,e2,t,type);
    }
    return e1;
}

static int
expr12(void)
{
    int e1,e2,t,op;

    e1=expr13();
    while((op=sym)==MUL||op==DIV||op==MOD) {
	conv->op_(sym);
	e1=rvalue(e1);
	t=type;
	getsym(0);
	e2=rvalue(expr13());
	e1=binop(op,e1,e2,t,type);
    }
    return e1;
}

/* unary operators */

static int
expr13(void)
{
    int e,op,dir;
    NMTBL *nptr1;

    switch (op = sym) {
    case INC: case DEC:
	conv->prefix_(sym);
	getsym(0);
	lcheck(e=expr13());
	dir = op==INC?1:-1;
	switch(type) {
	case CHAR:
	    type= INT;  return(list4(PREINC,e,dir,1));
	case UCHAR:
	    type= UNSIGNED; return(list4(UPREINC,e,dir,1));
	case SHORT:
	    type= INT;  return(list4(PREINC,e,dir,size_of_short));
	case USHORT:
	    type= UNSIGNED;  return(list4(UPREINC,e,dir,size_of_short));
	case INT:
	    return(list4(PREINC,e,dir,size_of_int));
	case UNSIGNED:
	    return(list4(UPREINC,e,dir,size_of_int));
#if LONGLONG_CODE
	case LONGLONG:
	    return(list4(LPREINC,e,dir,size_of_longlong));
	case ULONGLONG:
	    return(list4(LUPREINC,e,dir,size_of_longlong));
#endif
#if FLOAT_CODE
	case FLOAT:
	    return(list3(FPREINC,e,dir));
	case DOUBLE:
	    return(list3(DPREINC,e,dir));
	}
#endif
	if(integral(type))
	    return(list4(PREINC,e,dir,size_of_int));
	if(type>0 && car(type)==BIT_FIELD) {
	    e = list4(BPREINC,e,dir,type);
	    type = cadr(type); /* value type */
	    return e;
	}
	if(car(type)!=POINTER)
	    error(TYERR);
	return(list4(UPREINC,e,dir*size(cadr(type)),size_of_int ));
    case MUL: /* *p */
	conv->prefix_(sym);
	getsym(0);
	e=rvalue(expr13());
	return(indop(e));
    case BAND: /* &p */
	conv->prefix_(sym);
	getsym(0);
	switch(car(e=expr13())) {
	case INDIRECT:
	    e=cadr(e);
	    break;
	case DREGISTER:  /* should be error? */
	case FREGISTER:
	case LREGISTER:
	case REGISTER:
	case GVAR:
	case LVAR:
	    e=list2(ADDRESS,e);
	    break;
	case FNAME:
	    break;
	default:error(LVERR);
	}
	type=list2(POINTER,type);
	return e;
    case SUB:  /* -p */
	conv->prefix_(sym);
	getsym(0);
	e=rvalue(expr13());
#if FLOAT_CODE
	if(type==FLOAT) {
	    return(car(e)==FCONST?dlist2(FCONST,-dcadr(e)):list2(FMINUS,e));
	} else if(type==DOUBLE) {
	    return(car(e)==DCONST?dlist2(DCONST,-dcadr(e)):list2(DMINUS,e));
	}
#endif
#if LONGLONG_CODE
	if(type==LONGLONG||type==ULONGLONG) {
	    // return list2(LMINUS,e);
	    if (car(e)==LCONST) {
		if (lcadr(e)>0 && type==ULONGLONG) { type=LONGLONG;
		} else if (lcadr(e)<=0 && type==LONGLONG) { type=ULONGLONG;
		}
		return llist2(LCONST,-lcadr(e));
	    }
	    return list2(LMINUS,e);
	}
#endif
	if(!integral(type))
	    error(TYERR);
	if (car(e)==CONST) {
	    if (cadr(e)>0 && type==UNSIGNED) { type=INT;
	    } if (cadr(e)<=0 && type==INT) { type=UNSIGNED;
	    } 
	    return list2(CONST,-cadr(e));
	}
	return list2(MINUS,e);
    case BNOT: /* ~p */
	conv->prefix_(sym);
	getsym(0);
	e=rvalue(expr13());
        // LONGLONG?
	if(!integral(type))
	    error(TYERR);
	return(car(e)==CONST?list2(CONST,~cadr(e)):list2(BNOT,e));
    case LNOT: /* !p */
	conv->prefix_(sym);
	getsym(0);
	e=rvalue(expr13());
	type=INT;
#if FLOAT_CODE
	if (car(e)==DCONST||car(e)==FCONST) return list2(CONST,!dcadr(e));
#endif
#if LONGLONG_CODE
	if (car(e)==LCONST) return list2(CONST,!lcadr(e));
#endif
	if (car(e)==CONST) return list2(CONST,!cadr(e));
	if(!scalar(type))
	    error(TYERR);
	return list2(LNOT,e);
    case ALLOCA:
	conv->prefix_(sym);
	type=POINTER;
	getsym(0);
	checksym(LPAR);
	e=expr0();
	checksym(RPAR);
	return list2(ALLOCA,rvalue(e));
    case SIZEOF:
	conv->prefix_(sym);
	if(getsym(0)==LPAR) {
	    if(typeid(getsym(0))) {
		e=list2(CONST,size(typename()));
		type=INT;
		checksym(RPAR);
		return e;
	    } else {
		e=expr0();
		checksym(RPAR);
		expr16(e);
		if(sym==INC||sym==DEC) {   
                    /* after this operation, type is extended */
		    getsym(0);
		    switch(type) {
		    case CHAR:   type=INT; break;
		    case SHORT:  type=INT; break;
		    case UCHAR:  type=UNSIGNED; break;
		    case USHORT: type=UNSIGNED; break;
		    case FLOAT:
		    case DOUBLE: break;
		    default:
			if(!scalar(type))
			    error(TYERR);
		    }
		}
	    }
	} else
	    expr13();
	e=list2(CONST,size(type));
	type=INT;
	return e;
    case LAND: /* &&p  gcc extension label value */
	getsym(0);
	e = expr13();
	type = car(e);
	if (type!=FNAME) {
	    error(TYERR); 
	}
        nptr1 = (NMTBL *)cadr(e);
        type = nptr1->sc;
        if (type==EMPTY||type==EXTRN1||type==EXTRN) {
            nptr1->sc=EMPTY;
            nptr1=l_top_search(nptr->nm,0);
            nptr1->sc = FLABEL;
            nptr1->dsp = fwdlabel();
        }
	type = list2(POINTER,VOID);
	return list2(LABEL,nptr1->dsp);
    }
    e=expr14();

    /* postfix unary operators */

    if((op=sym)==INC||op==DEC) {
	conv->postfix_(sym);
	lcheck(e);
	getsym(0);

	dir = op==INC?1:-1;
	switch(type) {
	case CHAR:   type= INT;      return(list4(POSTINC,e,dir,1));
	case UCHAR:  type= UNSIGNED; return(list4(UPOSTINC,e,dir,1));
	case SHORT:  type= INT;      return(list4(POSTINC,e,dir,size_of_short));
	case USHORT: type= UNSIGNED; return(list4(UPOSTINC,e,dir,size_of_short));
	case INT:                    return(list4(POSTINC,e,dir,size_of_int));
	case UNSIGNED:               return(list4(UPOSTINC,e,dir,size_of_int));
#if FLOAT_CODE
	case FLOAT:                  return(list3(FPOSTINC,e,dir));
	case DOUBLE:                 return(list3(DPOSTINC,e,dir));
#endif
#if LONGLONG_CODE
	case LONGLONG:               return(list3(LPOSTINC,e,dir));
	case ULONGLONG:              return(list3(LUPOSTINC,e,dir));
#endif
	}
	if(integral(type))
	    return(list4(POSTINC,e,dir,size_of_int));
	if(type>0 && car(type)==BIT_FIELD) {
	    e = list4(BPOSTINC,e,dir,type);
	    type = cadr(type); /* value type */
	    return e;
	}
	if(car(type)!=POINTER)
	    error(TYERR);
	return(list4(UPOSTINC,e,
	    op==INC?size(cadr(type)):-size(cadr(type)),size_of_int ));
    }
    return e;
}

/* mark extern symbol is used */

static void
extrn_use(NMTBL *nptr)
{
    /* EXTRN1 means that defined extern is used in this source */
    if(nptr->sc==EXTRN) {
	nptr->sc=EXTRN1;
    }
    if (!nptr->next) {
	nptr->next = global_list; global_list = nptr;
    }
}

/*  define function name as extern */

static int
fname(NMTBL *nptr)
{
    int e1;
    e1=list2(FNAME,(int)nptr);
    // type=list3(FUNCTION,type,arg);
    type=list3(car(nptr->ty),cadr(nptr->ty),caddr(nptr->ty));
    getsym(0);
    extrn_use(nptr);
    return expr16(e1);
}

/* term */

static int
expr14(void)
{
    int e1=0,t,t1,smode;
    NMTBL *nptr0;

    switch(sym) {
    case IDENT:
	conv->id_(sym,nptr);
	switch(nptr->sc) {
	case EXTRN: case EXTRN1: 
	    extrn_use(nptr);
	case STATIC:
	    if(is_code(nptr)||is_function(nptr)) {
		return fname(nptr);
	    }
	case GVAR:
	    e1=list3(GVAR,0,(int)nptr);
	    type=nptr->ty;
	    getsym(0);
	    extrn_use(nptr);
	    break;
	case FLABEL: case BLABEL:
	case FUNCTION: case CODE:
	    return fname(nptr);
	case LVAR:
	    e1=list2(LVAR,nptr->dsp);
	    type=nptr->ty;
	    getsym(0);
	    break;
	case LREGISTER:
	case DREGISTER:
	case FREGISTER:
	case REGISTER:
	    e1=list3(nptr->sc,nptr->dsp,(int)nptr);
	    type=nptr->ty;
	    getsym(0);
	    break;
	case ENUM:
	    e1=list2(CONST,nptr->dsp);
	    type=INT;
	    getsym(0);
	    break;
	case EMPTY:
	    if(getsym(0)==LPAR) {
		type= glist3(FUNCTION,INT,0);
                nptr->sc = EXTRN1;
		nptr->ty= type;
		extrn_use(nptr);
		e1=expr15(list2(FNAME,(int)nptr));
		break;
	    } else if (in_macro_if) {
		type = INT;
		e1= list2(CONST,0);
		break;
	    } else {
		nptr->sc = EXTRN1;
		nptr->ty= glist3(FUNCTION,INT,0);
		e1=list2(FNAME,(int)nptr);
		type=list3(nptr->sc,nptr->ty,nptr->dsp);
		break;
	    }
	default:error(UDERR);
	}
	break;
    case STRING:
	conv-> string_(nptr->nm,nptr->dsp);
	e1=list3(STRING,(int)nptr->nm,nptr->dsp);
	type=list3(ARRAY,CHAR,nptr->dsp);
	getsym(0);
	break;
    case CONST:
	conv-> const_(symval);
	type= symval>=0?UNSIGNED:INT;
	e1=list2(CONST,symval);
	getsym(0);
	break;
#if FLOAT_CODE
    case FCONST:
	conv-> const_(symval);
	type= FLOAT;
	e1=dlist2(FCONST,dsymval);
	getsym(0);
	break;
    case DCONST:
	conv-> const_(symval);
	type= DOUBLE;
	e1=dlist2(DCONST,dsymval);
	getsym(0);
	break;
#endif
#if LONGLONG_CODE
    case LCONST:
	conv-> const_(symval);
	type= ULONGLONG;
	e1=llist2(LCONST,lsymval);
	getsym(0);
	break;
#endif
    case RETURN:
	conv-> return_f_();
	if (!is_function(fnptr)) {
	    error(STERR);
	}
	type=list2(POINTER,CODE);
	e1=list2(RETURN,(int)fnptr);
	getsym(0);
	break;
    case DEFINED:
	t = mode; mode = IFDEF;
	getsym(0);
	if (sym==LPAR) { t1 = 1; getsym(0); } else t1 = 0;
	conv-> defined_(nptr->nm);
	mode = t;
	type= INT;
	e1=list2(CONST,symval);
	getsym(0);
	if (t1)
	    checksym(RPAR);
	break;
    case ENVIRONMENT:
	conv-> environment_();
	type=list2(POINTER,VOID);
	e1=list2(ENVIRONMENT,0);
	getsym(0);
	break;
    case LPAR:
	conv->lpar_();
	getsym(0);
	if(sym==VOLATILE) getsym(0);

	/* type cast */

	if(typeid(sym)) {
	    t=typename();
	    conv->return_type_(t,0,0);
	    conv->rpar_();
	    checksym(RPAR);
	    if (sym==LC && (t>0 && (car(t)==STRUCT||car(t)==UNION))) {
		//             q->lock = (spinlock_t) { };
		smode = mode;
		type = t;
		nptr0=new_static_name("__lstruct",'_');
		nptr0->next = local_static_list; local_static_list = nptr0;
		nptr0->sc = GVAR;
		e1 = size(type);
		nptr0->ty = type;
		mode=STADECL;
		decl_data_field(type,nptr0,0);
		checksym(RC);
		e1 = list3(RSTRUCT,list3(GVAR,0,(int)nptr0),e1);
		mode = smode;
		return e1;
	    }
	    e1=expr13();
	    return correct_type(e1,t);
	} else if (sym==LC) {
	    // statement in expression  (GNU extension)
	    docomp(1);
	    e1 = lastexp; lastexp = 0;
	} else {
	    e1=expr0();
	    conv->rpar_();
	}
	checksym(RPAR);
	break;
    default:error(EXERR);
    }
    return expr16(e1);
}

/* post fix binary operator (struct . -> or array[] */

static int
expr16(int e1)
{
    int e2,t,ind;

    while(1) {
       if(sym==LBRA) {  /* a[3] */
	    conv->lbra_(sym);
	    e1=rvalue(e1);
	    t=type;
	    getsym(0);
	    e2=rvalue(expr0());
	    checksym(RBRA);
	    conv->rbra_(sym);
	    e1=binop(ADD,e1,e2,t,type);
	    e1=indop(e1);
        } else if(sym==LPAR) { 
	    e1=expr15(e1); /* f() */
	} else if(sym==PERIOD||sym==ARROW) { 
	    ind = sym;
	    conv->op_(sym);
	    getsym(0);
	    if (sym!=IDENT) error(TYERR);
	    conv->id_(sym,nptr);
	    e1=strop(e1,ind==ARROW);
	    getsym(0);
	} else break;
    }
    if(car(e1)==FNAME) type=list2(POINTER,type);
    return e1;
}

/* function call */

static int
expr15(int e1)
{
    int t,arglist,e,sz,argtypes,at,ftype;

    /* function call target */

    if(car(type)==POINTER) {
	if (car(cadr(type))==FUNCTION||car(cadr(type))==CODE) {
	    e1=rvalue(e1);
	    type=cadr(type);
	} /* else error */
    }
    if(integral(type)|| ((car(type)!=FUNCTION)&&(car(type)!=CODE))) {
	error(TYERR);
    }
    ftype = type;
    conv->funcall_(type);

    /* function argments */

    argtypes = caddr(type);
    if ((t=cadr(type))>=0 && (car(t)==STRUCT||car(t)==UNION)) {
	/* skip return struct pointer */
	if (argtypes==0) error(-1);
	argtypes = cadr(argtypes);
    }
    arglist=0;
    getsym(0);
    while(sym!=RPAR) {
	e=rvalue(expr1());
	if(argtypes==0) at=DOTS;
	else if(car(argtypes)==DOTS) at=DOTS;
        else { at=car(argtypes); argtypes=cadr(argtypes); }
	e = correct_type(e,at);
	arglist=list3(e,arglist,type);
	if(sym!=COMMA) break;
	conv->comma_();
	getsym(0);
    }
    checksym(RPAR);
    conv->funcall_args_();
    if(car(t)==CODE)
	return list4(FUNCTION,e1,arglist,ftype);

    /* return type */

    type = cadr(ftype); 
    if(type==CHAR) type=INT;
    else if(car(type)==STRUCT||car(type)==UNION) {
	/* make temporary struct for return value */
	/* but it is better to see we can reuse old one */
	if (tmp_struct) {
	    sz = size(tmp_struct->ty);
	    if (sz>=size(type)) {
		/* reuse it */
	    } else if (tmp_struct->dsp-sz==disp) {
		/* extendable */
		disp -= tmp_struct->dsp-sz;
		tmp_struct->dsp = disp;
	    } else {
		tmp_struct = def(0);
	    }
	} else {
	    tmp_struct = def(0);
	}
	e = list2(LVAR,tmp_struct->dsp);

	/* pass the pointer as an argument */
	/* this is recognized by called function declaration */
	/* but I don't know this sequence is compatible with gcc */

	arglist = append3(arglist,list2(ADDRESS,e),list2(POINTER,type));
    }
    return list4(FUNCTION,e1,arglist,ftype);
}

static int
typeid(int s)
{
    switch(s) {
	case CODE  : case SHORT :
	case LONG  : case STRUCT  : case UNION  : case ENUM :
	case LONGLONG  : case FLOAT  : case DOUBLE  : case VOID :
	case ULONGLONG   : case TYPEOF : case KONST:
	    return 1;
	case IDENT: return nptr->sc==TYPE;
    }
    return (integral(s));
}

static int
typename(void)
{
    int t;

    type=t=typespec();
    ndecl0();
    reverse(t);
    return type;
}

/*
    type prefix in cast
 */

static int
ndecl0(void)
{
    if(sym==MUL) {
	getsym(0);
	return type=list2(POINTER,ndecl0());
    }
    return ndecl1();
}

/*
    type postfix in cast
 */

static int
ndecl1(void)
{
    int i,t,arglist;

    if(sym==LPAR) {
	if(getsym(0)==RPAR) {
	    type=list3(FUNCTION,type,0); getsym(0);
	} else {
	    ndecl0();
	    checksym(RPAR);
	}
    }
    while(1) {
	if(sym==LBRA) {
	    getsym(0);
	    t=type;
	    i=cexpr(expr(1));
	    checksym(RBRA);
	    type=list3(ARRAY,t,i);
	} else if(sym==LPAR) {
	    t = type;
	    getsym(0);
	    arglist=0;
	    while(sym!=RPAR) {
		ndecl0();
		arglist=list2(type,arglist);
		if(sym!=COMMA) break;
		getsym(0);
	    }
	    checksym(RPAR);
	    type=list3(FUNCTION,t,arglist);
	}
	else return type;
    }
}

/*
     string heap management
 */

static struct cheap *
new_cheap()
{
    struct cheap *p = (struct cheap *)malloc(sizeof(struct cheap));
    if (!p) error(MMERR); // fatal
    p->ptr = p->first = (char *)malloc(CHEAPSIZE);
#if HEAP_REPORT
    fprintf(stderr,"** new cheap %d\n",(int)CHEAPSIZE);
#endif
    p->last = p->first + CHEAPSIZE;
    if (!p->ptr) error(MMERR); // fatal
    p->next = 0;
    return p;
}

extern struct cheap *
increment_cheap(struct cheap *cheap,char **save)
{
    char *p,*q,*from,*to;
    int i;
    if (cheap->ptr == cheap->last) {
	if (!cheap->next) { 
	    cheap->next = new_cheap();
	}
	if (save) {
	    cheap->ptr = from = *save;
	    i = cheap->last - from;
	    to   = *save = cheap->next->first;
	    if (i>CHEAPSIZE) error(NMERR);
	    if (to<=chptr && chptr<=to+i) {
		if (!cheap->next->next) { 
		    cheap->next->next = new_cheap();
		}
		q = chptr;
		p = chptr = cheap->next->next->ptr;
		while((*p++ = *q++));
	    }
	    while(i-->0) {
		*to++ = *from++;
	    }
	}
	cheap = cheap->next;
    }
    cheap->ptr++;
    return cheap;
}

/* mark string cheap */
extern void
save_cheap(struct cheap *scheap,struct cheap *cheap)
{
    // keep curret cheap pointer
    scheap->ptr = cheap->ptr;
    scheap->next = cheap;
}

/* discard string heap until marked string */
extern struct cheap *
reset_cheap(struct cheap *scheap)
{
    // go back to the kept curret cheap pointer
    if (cheap==scheap->next) {
	cheap->ptr = scheap->ptr;
    } else {
	cheap->ptr = cheap->first;
	cheap = scheap->next;
	cheap->ptr = scheap->ptr;
    }
    return cheap;
}

/*
     Symbol table handling
 */

static int
neqnamel(char *p,char *q,int len)
{
    while(len-->0 && *p++ == *q++);
    return len!=-1;
}

static NMTBL *
hash_search(char *name,struct cheap *scheap,int len,unsigned int hash,int mode)
{
    NMTBL *hptr,**iptr,**eptr;

    eptr = iptr= &htable[hash % GSYMS];
    while((hptr=*iptr) && hptr->sc!=0 && neqnamel(hptr->nm,name,len)) {
	if (++iptr== &htable[GSYMS])
	    iptr=&htable[0];
	if (eptr==iptr) error(GSERR);
    }
    if (!hptr) {
	if (mode==NONDEF) return 0;
	hptr = get_nptr();
	hptr->nm = name; /* name should be in the safe place (cheap) */
	*iptr = hptr;
    }
    if (hptr->sc == 0) {
	hptr->sc=EMPTY;
    } else {
	cheap = reset_cheap(scheap);
    }
    return hptr;
}

extern NMTBL *
get_name(char *name,int *len,int mode)
{
    /* no name copy */
    unsigned int ch,i = 0;
    unsigned int hash0 = 0;
    unsigned char *n = name;
    struct cheap scheap;

    save_cheap(&scheap,cheap);
    ch = *n++;
    for(i=0;alpha(ch) || digit(ch);i++) {
	hash_value(hash0,ch);
	ch = *n++;
    }
    if (len) *len = i;
    return hash_search(name,&scheap,i,hash0,mode);
}

extern NMTBL *
get_name_from_chptr()
{
    int i = 0;
    unsigned int hash0 = 0;
    char *name = cheap->ptr;
    struct cheap scheap;

    save_cheap(&scheap,cheap);
    for(i=0;alpha(ch) || digit(ch);i++) {
	hash_value(hash0,*cheap->ptr = ch);
	cheap = increment_cheap(cheap,&name);
	getch();
    }
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,&name);
    return hash_search(name,&scheap,i,hash0,DEF);
}

static void
getstring(void)
{
    char *name = cheap->ptr;
    int i= 0;
    unsigned int hash = 0;
    struct cheap scheap;

    save_cheap(&scheap,cheap);
    while (ch == '"') {
	in_quote = 1;
	getch();
	while (ch != '"') {
	    if (i>STRSIZE) error(STRERR);
	    hash_value(hash, *cheap->ptr = escape());
	    cheap = increment_cheap(cheap,&name);
	    i++;
	}
	in_quote = 0;
	getch();
	skipspc();  // "aaa" "bbb" case
    }
    in_quote = 0;
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,&name);
    i++;
    nptr = name_space_search(hash_search(name,&scheap,i,hash,DEF),STRING);
    symval = i;
}

/*
   long long posfix (0ULL etc.)
 */
static int
is_ll()
{
    if (ch=='U' || ch=='u') {
	getch();
    }
    if (ch=='L'||ch=='l') {
	if (getch()=='L'||ch=='l') {
	    getch();
	    return 1;
	}
    }
    return 0;
}

static int
get_numerical()
{
    int d;
    struct cheap scheap;
    char *num;

    /* numerical */

    save_cheap(&scheap,cheap);
    symval=0; d=0;
    num = cheap->ptr;
    sym = 0;
    if(ch=='.') {
	getch();
	if(ch=='.') {
	    getch();
	    if (ch=='.') {
		getch();
		return sym=DOTS;
	    }
	    error(CHERR);
	    return getsym(0);
	} else if (!digit(ch))
	    return sym=PERIOD;
	d=1;
	*cheap->ptr = '.'; /* .0 case */
	cheap = increment_cheap(cheap,&num);
    } else if (ch == '0') {
	*cheap->ptr = ch; cheap = increment_cheap(cheap,&num);
	if (getch() == 'x' || ch == 'X') {
	    *cheap->ptr = ch; cheap = increment_cheap(cheap,&num);
	    /* hexadicimal */
	    while(1) {
		getch(); *cheap->ptr = ch;
		cheap = increment_cheap(cheap,&num);
		if(digit(ch))
		    symval=symval*16+ch-'0';
		else if('a'<=ch&&ch<='f')
		    symval=symval*16+ch-'a'+10;
		else if('A'<=ch&&ch<='F')
		    symval=symval*16+ch-'A'+10;
		else break;
	    }
	    if (is_ll()) {
#if LONGLONG_CODE
		cheap->ptr[-1] = 0;
		lsymval = strtoll(num,0,0);
		// we should keep this value? like string?
		cheap = reset_cheap(&scheap);
		sym=LCONST;
#endif
	    } else
		sym=CONST;
	} else if (digit(ch)) {
	    /* octal */
	    while(1) {
		getch(); *cheap->ptr = ch;
		cheap = increment_cheap(cheap,&num);
		if(digit(ch))
		    symval=symval*8+ch-'0';
		else break;
	    }
	    if (is_ll()) {
#if LONGLONG_CODE
		cheap->ptr[-1] = 0;
		cheap = increment_cheap(cheap,&num);
		lsymval = strtoll(num,0,0);
		cheap = reset_cheap(&scheap);
		sym=LCONST;
#endif
	    } else 
		sym=CONST;
	} else if (ch=='L'||ch=='U') {  /* 0L or 0LL case */
	    if (is_ll()) {
#if LONGLONG_CODE
		lsymval = 0;
		sym=LCONST;
#endif
	    }
	} else if (ch=='.'||ch=='e') {
	    d=1;
	    *cheap->ptr = '0'; /* 0. case */
	    cheap = increment_cheap(cheap,&num);
	} else {
	    cheap = reset_cheap(&scheap);
	    symval = 0;
	    sym=CONST;
	}
    } else {
	while(digit(ch)) {
	    *cheap->ptr = ch;
	    cheap = increment_cheap(cheap,&num);
	    symval=symval*10+ch-'0';getch();
	}
	if (ch=='.'||ch=='e') d=1;
    }
    if (!sym) {
	if (!d) {
	    if (is_ll()) {
#if LONGLONG_CODE
		*cheap->ptr = 0;
		cheap = increment_cheap(cheap,&num);
		lsymval = strtoll(num,0,0);
		sym=LCONST;
#endif
	    } else
		sym=CONST;
	} else {
#if FLOAT_CODE
	    /* floating point case */
	    while(digit(ch)|| ch=='.'||ch=='e') {
		*cheap->ptr = ch;
		cheap = increment_cheap(cheap,&num);
		getch();
		if ((ch=='-' && cheap->ptr[-1]=='e')||
			(ch=='+' && cheap->ptr[-1]=='e')) {
		    *cheap->ptr = ch;
		    cheap = increment_cheap(cheap,&num);
		    getch();
		}
	    }
	    *cheap->ptr = 0;
	    cheap = increment_cheap(cheap,&num);
	    dsymval = strtod(num,0);
	    sym=DCONST;
#else
	    symval = 0;
	    sym=CONST;
#endif
	}
    }
    cheap = reset_cheap(&scheap);
    return sym;
}

/*
    Tokenizer
 */

extern int
getsym(int sc)
{
    NMTBL *nlist,*nptr0,*nptrm;
    char c;

    if (alpha(skipspc())) {
	nptrm=name_space_search(nlist = get_name_from_chptr(),MACRO);
	if (mode==MDECL) {
	    nptr = nptrm;
	    return (sym==MACRO);
	}
	if (mode==IFDEF) {
	    nptr = nptrm;
	    if (nptrm->sc == MACRO||nptrm->sc==FMACRO) {
		return (symval=1);
	    } else {
		return (symval=0);
	    }
	}
	if ((nptrm->sc==MACRO&&neqname((char *)car(nptrm->dsp),nptrm->nm)) ||
	    (nptrm->sc==FMACRO&&skipspc()=='(')) {
	    macro_expansion(nptrm);
	    return getsym(0);
	}
        /* global variable name table */
	nptr0 = name_space_search(nlist,sc);
	if (nptr0->sc == RESERVE) return sym = nptr0->dsp;
	sym = IDENT;
	gnptr=nptr=nptr0;

	if (mode==ADECL && nptr0->sc ==TYPE) return sym;
	if (mode==GDECL || mode==GSDECL || mode==GUDECL ||
	    mode==GTDECL || mode==TOP || mode==GEDECL) {
	    return sym;
	}
	if (nptr->sc == TYPE) return sym;
	if (nptr->sc == TAG) return sym;
	if (mode==STAT) {
	    /* can be undeclared global variable */
	    return sym;
	}
	/* define case */
	nptr = make_local_scope(nlist,nptr,sc);
	return sym;

    } else if (digit(ch)||ch=='.') {
	return get_numerical();
    } else if(ch=='\'') {
	getch();
	symval=escape();
	if(ch!='\'') error(CHERR);
	getch();
	return sym=CONST;
    } else if(ch=='"') {
	getstring();
	return sym= STRING;
    } 
    /* 2 letters literal */
    c=ch;
    getch();
    switch(c) {
    case '*':
	return postequ(MUL,MUL+AS);
    case '&':
	if(ch=='&') {getch();return sym=LAND;}
	return postequ(BAND,BAND+AS);
    case '-':
	if(ch=='>') {getch();return sym=ARROW;}
	if(ch=='-') {getch();return sym=DEC;}
	return postequ(SUB,SUB+AS);
    case '!':
	return postequ(LNOT,NEQ);
    case '~':
	return sym=BNOT;
    case '+':
	if(ch=='+') {getch();return sym=INC;}
	return postequ(ADD,ADD+AS);
    case '%':
	return postequ(MOD,MOD+AS);
    case '^':
	return postequ(EOR,EOR+AS);
    case '|':
	if(ch=='|') {getch();return sym=LOR;}
	return postequ(BOR,BOR+AS);
    case '=':
	return postequ(ASS,EQ);
    case '>':
	if(ch=='>') {getch();return postequ(RSHIFT,RSHIFT+AS);}
	return postequ(GT,GE);
    case '<':
	if(ch=='<') {getch();return postequ(LSHIFT,LSHIFT+AS);}
	return postequ(LT,LE);
    case '(':
	return sym=LPAR;
    case ')':
	return sym=RPAR;
    case '[':
	return sym=LBRA;
    case ']':
	return sym=RBRA;
    case '{':
	return sym=LC;
    case '}':
	return sym=RC;
    case ',':
	return sym=COMMA;
    case ':':
	return sym=COLON;
    case '?':
	return sym=COND;
    case ';':
	return sym=SM;
    case '/':
	if(ch=='/') {
	    in_comment = 1;
	    conv->comment_('/'); conv->comment_('/');
	    while(ch!='\n') { getch(); conv->comment_(ch); }
	    in_comment = 0;
	    getch();
	    return getsym(0);
	}
	if(ch!='*') return postequ(DIV,DIV+AS);
	in_comment = 1;
	conv->comment_('/'); conv->comment_('*');
	do {
	    c=ch; getch(); conv->comment_(ch);
	} while(!(c=='*'&&ch=='/'));
	in_comment = 0;
	getch();
	return getsym(0);
    case 0:
    case '\n':
    case '\f':
    case '\\':
	return getsym(0);
    default:
	error(CHERR);
	return getsym(0);
    }
}


static int
postequ(int s1, int s2)
{
    if(ch=='=') {getch();return sym=s2;}
    return sym=s1;
}

extern int
alpha(int c)
{
    return(('a'<=c&&c<='z')||('A'<=c&&c<='Z')||c=='_');
}

extern int
digit(int c)
{
    return('0'<=c&&c<='9');
}

extern void
free_nptr(NMTBL *n)
{
    n->dsp = (int)free_nptr_list;
    free_nptr_list = n;
}

extern NMTBL *
get_nptr()
{
    NMTBL *ret;
    if (free_nptr_list) {
	ret = free_nptr_list;
	free_nptr_list = (NMTBL *)free_nptr_list->dsp;
	ret->sc = 0;
	ret->dsp = 0;
	return ret;
    }
    if (nptr_pool->ptr >= nptr_pool->last) {
	if (nptr_pool->next) {
	    nptr_pool = nptr_pool->next;
	} else {
#if HEAP_REPORT
	    fprintf(stderr,"** nptr extended\n");
#endif
	    nptr_pool->next = new_cheap();
	    nptr_pool = nptr_pool->next;
	}
    }
    ret = ((NMTBL *)nptr_pool->ptr)++;
    ret->sc = 0;
    ret->dsp = 0;
    return ret;
}

/*
    dummy name table element
 */

int dummy_count = 0;

extern NMTBL *
anonymous_nptr()
{
    NMTBL *nptr;
    int i,j;
    nptr= get_nptr();
    j = dummy_count++;
    for(i=0;j>0;j/=10);
    nptr->nm = cheap->ptr;
    cheap->ptr += i-1+3;
    increment_cheap(cheap,&nptr->nm);
    nptr->nm[0]='_';
    nptr->nm[1]='a';
    nptr->nm[2]='n';
    j = dummy_count;
    for(;i>=0;i--) {
	nptr->nm[i+3]='0'+j%10;
	j/=10;
    }
    nptr->sc=EMPTY;
    nptr->dsp=0;
    return nptr;
}

extern NMTBL *
name_space_search(NMTBL *hash,int sc)
{
    int ns;
    NMTBL *n;

    for(ns=hash->dsp;ns;ns=cadr(ns)) {
	if (car(ns)==sc) {
	    return (NMTBL*)caddr(ns);
	}
    } 
    if (ns==0) {
	n = get_nptr();
	hash->dsp = glist3(sc,hash->dsp,(int)n);
    }
    n->nm = hash->nm;
    n->sc = EMPTY;
    n->dsp = 0;
    return n;
}

// search local nptr by name and storage class
extern NMTBL *
lsearch(char *name,int sc)
{
    NMTBL *nlist,*nptr1;
    nptr1 = name_space_search(nlist=get_name(name,0,DEF),sc);
    return make_local_scope(nlist,nptr1,sc);
}

// search top level local name (for label)
static NMTBL *
l_top_search(char *name,int sc)
{
    NMTBL *nlist,*nptr1;
    nptr1 = name_space_search(nlist=get_name(name,0,DEF),sc);
    return make_top_scope(nlist,nptr1,sc);
}

extern NMTBL *
make_local_scope(NMTBL *nlist,NMTBL *nptr1,int sc)
{
    int ns;
    for(ns=nlist->dsp;ns;ns=cadr(ns)) {
	if (car(ns)==sc /* && nptr1->sc!=EMPTY */) {
	    // memorize previous nptr for this name for leave_scope
	    car(current_scope) = glist3(ns,car(current_scope),(int)nptr1);
	    caddr(ns) = (int)(nptr1 = get_nptr());
	    nptr1->nm = nlist->nm; nptr1->sc=EMPTY; nptr1->dsp = 0;
	}
    }
    return nptr1;
}

static NMTBL *
make_top_scope(NMTBL *nlist,NMTBL *nptr1,int sc)
{
    int ns;
    int scope;
    for(scope=current_scope;cadr(scope);scope=cadr(scope));
    for(ns=nlist->dsp;ns;ns=cadr(ns)) {
	// memorize previous nptr for this name for leave_scope
	if (car(ns)==sc /* && nptr1->sc!=EMPTY */) {
	    car(scope) = glist3(ns,car(scope),(int)nptr1);
	    caddr(ns) = (int)(nptr1 = get_nptr());
	    nptr1->nm = nlist->nm; nptr1->sc=EMPTY; nptr1->dsp = 0;
	}
    } 
    return nptr1;
}

extern  void
enter_scope()
{
    current_scope = glist2(0,current_scope);
}

extern  void
leave_scope()
{
    NMTBL *ns;
    int scope,next;
    if (!current_scope) error(-1);
    // restore nptr of current scope name to the previous nptr
    scope = car(current_scope);
    while(scope) {
	ns = (NMTBL *)caddr(car(scope));
	if (ns->sc != GVAR) free_nptr(ns);
	caddr(car(scope)) = caddr(scope);
	next = cadr(scope);
	free_glist2(scope); // will destroy cadr
	scope = next;
    }
    if ((next=cadr(current_scope))) {
	free_glist2(current_scope);
	current_scope = next;
    } else
	car(current_scope) = 0;
}

extern void
extern_define(char *s,int d,int type,int use)
{
    NMTBL *nptr0;

    (nptr0 = name_space_search(get_name(s,0,DEF),0))->sc = EXTRN;
    nptr0->dsp = d; nptr0->ty=type;
    if (use) extrn_use(nptr0);
}

extern int
neqname(char *p,char *q)
{
    if (!p)
	return 0;
    while(*p && *p!='.')
	    if(*p++ != *q++) return 1;
    return (*q!=0);
}

extern int
skipspc(void)
{
    static int topspc = 0;

    while(ch=='\t'||ch=='\n'||ch==' '||ch=='\r') {
	if (ch=='\n'||ch=='\r') topspc=1;
	if (topspc)
	    conv->comment_(ch);
	getch();
    }
    topspc=0;
    return ch;
}

extern int
getch(void)
{
    int i,j;
    if(*chptr) 
	return ch = *chptr++;
    else if (chptrsave) {
	chptr = (char *)car(chptrsave);
	ch = car(chsave);
	i = cadr(chptrsave);
	j = cadr(chsave);
        free_glist2(chptrsave);
        free_glist2(chsave);
	chptrsave = i;
	chsave = j;
	return ch;
    }
    getline();
    if (in_macro_if) check_macro_eof();
    return getch();
}

static int
escape(void)
{
    char c;
    if ((c=ch) == '\\') {
	if (digit(c=getch())) {
	    c = ch-'0';
	    if (digit(getch())) {
		c = c*8+ch-'0';
		if (digit(getch())) {
		    c=c*8+ch-'0';getch();
		}
	    }
	    return c;
	}
	getch();
	switch(c) {
	case 'n': return '\n';
	case 't': return '\t';
	case 'b': return '\b';
	case 'r': return '\r';
	case 'f': return '\f';
	case '\\': return '\\';
	case '\n':
	    if (ch=='"') {
		return 0;
	    }
	    return escape();
	default:
	    return c;
	}
    }
    // if (c == '\n') error(EXERR);
    getch();
    return c;
}

/* node management (cdr coding ) */

#if LONGLONG_CODE

extern int
llist2(int e1, long long d1)
{
    int e;

    e=getfree((size_of_int+size_of_longlong)/size_of_int);
    heap[e]=e1;
    lcadr(e)=d1;
    return e;
}

extern int
llist3(int e1, int e2,long long d1)
{
    int e;

    e=getfree((size_of_int+size_of_longlong)/size_of_int);
    heap[e]=e1;
    heap[e+1]=e2;
    lcaddr(e)=d1;
    return e;
}

#endif

#if FLOAT_CODE

extern int
dlist2(int e1, double d1)
{
    int e;

    e=getfree((size_of_int+size_of_double)/size_of_int);
    heap[e]=e1;
    dcadr(e)=d1;
    return e;
}

extern int
dlist3(int e1, int e2,double d1)
{
    int e;

    e=getfree((size_of_int*2+size_of_double)/size_of_int);
    heap[e]=e1;
    heap[e+1]=e2;
    dcaddr(e)=d1;
    return e;
}

#endif

extern int
list2(int e1, int e2)
{
    int e;

    e=getfree(2);
    heap[e]=e1;
    heap[e+1]=e2;
    return e;
}

extern int
list3(int e1, int e2, int e3)
{
    int e;

    e=getfree(3);
    heap[e]=e1;
    heap[e+1]=e2;
    heap[e+2]=e3;
    return e;
}

extern int
list4(int e1, int e2, int e3, int e4)
{
    int e;

    e=getfree(4);
    heap[e]=e1;
    heap[e+1]=e2;
    heap[e+2]=e3;
    heap[e+3]=e4;
    return e;
}

static int
getfree(int n)
{
    int e;

    switch (mode) {
	case GDECL: case GSDECL: case GUDECL: case GTDECL:
	case MDECL: case ADECL: case LSDECL: case LUDECL: case GEDECL:
	e=gfree;
	gfree+=n;
	break;
    default:
	lfree-=n;
	e=lfree;
    }
    if(lfree<gfree) error(HPERR);
    return e;
}

static int free_glist2_list = 0;
static int free_glist3_list = 0;

extern int
glist2(int e1,int e2)
{
    int smode,ret;
    if (free_glist2_list) {
	ret = free_glist2_list;
	free_glist2_list = cadr(free_glist2_list);
	car(ret)=e1; cadr(ret)=e2;
	return ret;
    }
    smode = mode;
    mode = GDECL;
    ret = list2(e1,e2);
    mode = smode;
    return ret;
}

extern void
free_glist2(int e1)
{
    if (e1>gfree) return;  /* freeing local heap */
    if (e1==gfree) {
	gfree-=2;
    } else {
	cadr(e1) = free_glist2_list;
	free_glist2_list = e1;
    }
}

extern int
glist3(int e1,int e2,int e3)
{
    int smode,ret;
    if (free_glist3_list) {
	ret = free_glist3_list;
	free_glist3_list = cadr(free_glist3_list);
	car(ret)=e1; cadr(ret)=e2; caddr(ret)=e3;
	return ret;
    }
    smode = mode;
    mode = GDECL;
    ret = list3(e1,e2,e3);
    mode = smode;
    return ret;
}

extern void
free_glist3(int e1)
{
    if (e1>gfree) return;  /* freeing local heap */
    if (e1==gfree) {
	gfree-=3;
    } else {
	cadr(e1) = free_glist3_list;
	free_glist3_list = e1;
    }
}

extern void
free_glist3_a(int e1)
{
    int next;
    while(e1) {
	if (e1>gfree) continue;  /* freeing local heap */
	next = cadr(e1);
	if (e1==gfree) {
	    gfree-=3;
	} else {
	    cadr(e1) = free_glist3_list;
	    free_glist3_list = e1;
	}
	e1 = next;
    }
}


extern int
length(int list)
{
    int n=0;
    for(;list;n++) {
	list = cadr(list);
    }
    return n;
}

extern int
nth(int n, int list)
{
    while(n-->0) {
	list = cadr(list);
    }
    return list;
}

extern int
insert_ascend(int p,int e,int eq())
{
    int p1,p2,dup;
    if(!p) return e;
    if (car(p)==car(e)) {
	if ((dup=eq())==0)  // duplicate value is not override
	    return p;
	else if (dup==2) {  // keep unique allow override
	    cadr(e) = cadr(p);  // skip one
	    return e;
	}                   // any other value allows duplicate
    } else if (car(p)>car(e)) {
	cadr(e) = p;
	return e;
    }
    p1=p;
    while(cadr(p)) {
	p = cadr(p2=p);
	if (car(p)==car(e)) {
	    if ((dup=eq())==0)  // duplicate value is not override
		return p1;
	    else if (dup==2) {  // keep unique allow override
		cadr(e) = cadr(p);  // skip one
		cadr(p2) = e;
		return p1;
	    }                   // any other value allows duplicate
	} else if (car(p)>=car(e)) {
	    cadr(e) = cadr(p2);
	    cadr(p2) = e;
	    return p1;
	}
    }
    cadr(p) = e;
    return p1;
}

extern int
append4(int p,int a1,int a2,int a3)
{
    int p1;
    if(!p) return list4(a1,0,a2,a3);
    p1=p;
    while(cadr(p)) p = cadr(p);
    cadr(p) = list4(a1,0,a2,a3);
    return p1;
}

extern int
append3(int p,int a1,int a2)
{
    int p1;
    if(!p) return list3(a1,0,a2);
    p1=p;
    while(cadr(p)) p = cadr(p);
    cadr(p) = list3(a1,0,a2);
    return p1;
}

extern void
display_ntable(NMTBL *n, char *s)
{
    fprintf(stderr,"\n%s ",s);
    fprintf(stderr,"nptr->sc %d ",n->sc);
    fprintf(stderr,"nptr->dsp %d ",n->dsp);
    fprintf(stderr,"nptr->ty %d ",n->ty);
    fprintf(stderr,"nptr->nm %s\n",n->nm);
}

/* for gdb... */

extern int c0(int d)  { fprintf(stderr,"heap[%d]=",d);return car(d); }
extern int c1(int d)  { fprintf(stderr,"heap[%d]=",d);return cadr(d); }
extern int c2(int d)  { fprintf(stderr,"heap[%d]=",d);return caddr(d); }
extern int c3(int d)  { fprintf(stderr,"heap[%d]=",d);return cadddr(d); }
extern char *cc0(int d) { fprintf(stderr,"heap[%d]=",d);return (char *)car(d); }
extern char *cc1(int d) { fprintf(stderr,"heap[%d]=",d);return (char *)cadr(d); }
extern char *cc2(int d) { fprintf(stderr,"heap[%d]=",d);return (char *)caddr(d); }
extern char *cc3(int d) { fprintf(stderr,"heap[%d]=",d);return (char *)cadddr(d); }

/* end */