view mc-parse.c @ 725:3f1f6c0610c1

goto with enviornment syntax changed.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Thu, 20 Aug 2009 16:39:02 +0900
parents 76761a18703b
children 1f6e34c4dbbf
line wrap: on
line source

/************************************************************************
** Copyright (C) 2006 Shinji Kono
** 連絡先: 琉球大学情報工学科 河野 真治  
** (E-Mail Address: kono@ie.u-ryukyu.ac.jp)
**
**    このソースのいかなる複写,改変,修正も許諾します。ただし、
**    その際には、誰が貢献したを示すこの部分を残すこと。
**    再配布や雑誌の付録などの問い合わせも必要ありません。
**    営利利用も上記に反しない範囲で許可します。
**    バイナリの配布の際にはversion messageを保存することを条件とします。
**    このプログラムについては特に何の保証もしない、悪しからず。
**
**    Everyone is permitted to do anything on this program 
**    including copying, modifying, improving,
**    as long as you don't try to pretend that you wrote it.
**    i.e., the above copyright notice has to appear in all copies.  
**    Binary distribution requires original version messages.
**    You don't have to ask before copying, redistribution or publishing.
**    THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
***********************************************************************/

#include <stdio.h>
// #include <stdlib.h>  // for malloc

#include "mc.h"
#include "mc-parse.h"
#include "mc-codegen.h"
#include "mc-switch.h"
#include "mc-macro.h"
#include "mc-inline.h"
#include "conv/conv.h"

extern Converter *conv;

#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

int parse_mode = 1; // generate parse tree for all code

static int HEAP_REPORT = 0;
static int lfree_type_limit;     // debugging purpose
				 // list of local declared type have to be
				 // keep

NMTBL null_nptr;
NMTBL *fnptr;                    // current compiling function
NMTBL *nptr,*gnptr;              // current name ptr
NMTBL *local_nptr;               // local variable initialzation

static NMTBL *htable0[GSYMS];    // hash table
NMTBL **htable = htable0;

// string pool
struct cheap *cheap,*cheap0;
static struct cheap *nptr_pool,*nptr_pool0;
static NMTBL *free_nptr_list;

static int current_scope;         // scope unwinder

int attribute;                    // current attribte list

int inline_funcs;                 // list of inline functions (for pfdecl)

// current linebuffer and pointer
char linebuf[LBUFSIZE];
char *chptr;
int ch;                           // pre-read character
int chptrsave,chsave;             // linebuffer stack (for macro)

int chk;
int args;                         // function argument list
int init_vars;                    // list of local variable initialize
/*
     heap 0---global----gfree->  <-lfree--local----TOP (heapsize)
       lfree is reset in top level
 */
int *heap;                        // heap pointer
int lfree;                        // local heap top
int gfree;                        // global heap top
int heapsize = HEAPSIZE;
int asmf;                         // in asm (no longer used)
int bit_field_disp;
int blabel,dlabel;                // break label, default label
int clabel;                       // continue label
int cslabel;                      // case label list
int csvalue1;
int cslist;
int control;                      // execution will be reached here
int debug; 
int fields;                       // struct field list
int gtypedefed;                   // global typedef list (for mc-tree.c)
int typedefed;                    // local  typedef list (for mc-tree.c)
int in_comment;                   // getsym mode
int in_quote;                     // getsym mode
int labelno;                      // label number
int disp;                         // local variable offset
int gpc;                          // global variable count (nolonger used)
static int stat_no;               // local static unique number
int lastexp;                      // delayed last expression in a statement
int lineno;                       // lineno in a file
int glineno;                      // total lineno
int lsrc;                         //   source listing flag
int retlabel,retpending,retcont;  // return label
int pending_jmp;                  // previous jump (not yet generated)
int struct_return;                //    function returns struct
int sym,type;                     // current symbol and type
int mode,stmode,ctmode,inmode;    // mode, storage mode, constant mode, inline
int decl_str_init;                //   delayed local struct initialize
int parse;                        // parse tree list for inline

NMTBL *local_static_list,*global_list;   // list of static, global variable

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

//  function for Recursive decent nodes
static NMTBL *decl0(void),*decl1(void);
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);
extern 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();
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, int sc);
static void reverse(int t1);
static void set_converter(char *s);
static int escape(void);
static void statement(int);
static int typename(void);
static int decl_data_field(int type,int v,int offset);
static int decl_data(int t, int v,int offset,int skip);
static int typeid(int s);
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 void top_init();
static void qualifiers();
static void attributes();
static void macro_convert();
extern void sym_print(int,FILE *);
static void copy_attributes(NMTBL *n) ;

// current value of constant symbol

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

static int sdecl_f = 1;        // in struct defenition (for converter)
static int stypedecl;          // in type defenition (for converter)

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

static char *ccout = 0;        // output file name

//  include file search path
#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];   // keep track include file directory

static int ac,ac2;
static char **av;

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

/*
     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 'E':
	    ac2++;
	    macro_convert();
	    break;
	case 'd':
	    debug = 1;
	    break;
	case 'v':
	    HEAP_REPORT = 1;
	    break;
	case 'p':
	    parse_mode = !parse_mode;
	    break;
	case 'D':
	    //  define macro option. processed by macro
	    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) {
	top_init();
	while(getsym(0)==SM) conv->sm_();
	mode=GDECL;
	stmode=0; inmode=0; attribute = 0;
	args=0;
	decl();
    }
    /*NOTREACHED*/
}

static void
top_init()
{
//    int i;

    mode=TOP;
    if (gfree > heapsize-HEAPSIZE) {
	heapsize *= 2;
	if (HEAP_REPORT)
	    fprintf(stderr,"** heap extended to %d\n",heapsize);
	heap = (int*)realloc(heap,heapsize*sizeof(int));
	if(!heap) { error(MMERR); exit(1); }
    }
    set_lfree(heapsize);
    codegen_decl_init();
#if 0
    while (chptrsave!=0) {
	i = cadr(chptrsave); free_glist2(chptrsave); chptrsave = i;
    }
    while (chsave!=0) {
	i = cadr(chsave); free_glist2(chsave); chsave = i;
    }
#endif
    // a in previous extern f(a) is in current scope, release it
    leave_scope();
}

static void
macro_convert()
{
    init();
    top_init();
    while(getsym(0)) {
	// how to insert comment and new line?
	switch (sym) {
	case IDENT:
	    printf("%s",nptr->nm); break;
	case CONST:
	    printf("%d",symval); break;
#if FLOAT_CODE
	case FCONST:
	    printf("%g",dsymval); break;
	case DCONST:
	    printf("%g",dsymval); break;
#endif
#if LONGLONG_CODE
	case LCONST:
	    printf("%lld",lsymval); break;
#endif
	case STRING:
	    { int c; char *s = nptr->nm; int i=nptr->dsp;
		printf("\"");
		while(--i>0) {
		    c=*s++;
		    if(c=='\n') printf("\\n");
		    else if(c=='\r') printf("\\r");
		    else if(c=='\t') printf("\\t");
		    else if(c=='\e') printf("\\e");
		    else if(c=='"') printf("\\\"");
		    else if(c=='\\') printf("\\\\");
		    else if(!(' '<=c&&c<=0x7f)) printf("\\%03o",c);
		    else printf("%c",c);
		}
		printf("\"");
	    }
	    break;
	default:
	    sym_print(sym,stdout);
	}
	while (ch<=' ') {
	    putchar(ch); getch();
	}
    }
    /* NOT REACHED */
}

/*
    error handler
    when EOF, process next file
 */

static int
serious(int n)
{
    switch(n) {
    case DCERR:  // "Declaration syntax" :
    case STERR:  // "Statement syntax" :
    case EXERR:  // "Expression syntax" :
    case CNERR:  // "Constant required" :
    case CHERR:  // "Illegal character" :
    case MCERR:  // "Macro syntax" :
    case INCERR: // "Include syntax" :
    case TYERR:  // "Type mismatch" :
    case LVERR:  // "Lvalue required" :
    case UDERR:  // "Undeclared identifier" :
    case OPTION: // "Illegal option" :
    case INERR:  // "bad initialization" :
    case AGERR:  // "wrong number of arguments" :
    case CODE_ERR:  // "goto is necessary" :
    case ILERR:  // "inline error" :
    case SIERR:  // "non brace in struct init error" :
    case GTERR:  // "cannot goto to a function" :
    case FNERR:  // "calling a code segement, use goto" :
    case UCERR:  // "already used as code segement" :
    case UFERR:  // "already used as function" :
    case ENERR:  // function has return value but reached to the end
    case RETERR: // return in code segement is not allowed
    return 0;
    }
    return 1;
}

extern void
error(int n)
{
    char *file;
    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) {
	    closing();
	    if (chk) {
		fprintf(stderr, "Total internal labels : %u.\n",labelno-1);
		fprintf(stderr, "Total global variables: %u bytes.\n",gpc);
	    }
	    exit(0);
	}
    }
    if (conv->error_(n)) return;
    if (!filep) file = ""; else file = filep->name0;
    fprintf(stderr,"%s:%d:%s\n",file,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==AGERR) ? "wrong number of arguments" :
	(n==CODE_ERR) ? "goto is necessary" :
	(n==ILERR) ? "inline error" :
	(n==SIERR) ? "warning: missing braces around initializer" :
	(n==GTERR) ? "cannot goto to a function" :
	(n==FNERR) ? "calling a code segement, use goto" :
	(n==UCERR) ? "already used as code segement" :
	(n==UFERR) ? "already used as function" :
	(n==ENERR) ? "function has return value but reached to the end" :
	(n==CSERR) ? "no excutable code in switch" :
	(n==UFLDERR) ? "unknown field in struct/union" :
	(n==RETERR) ? "return in code segement is not allowed" :
	"Bug of compiler");
    errmsg();
    if (serious(n))
	exit(1);
}

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

    if(lineno==0) return;
    fprintf(stderr,"%s",linebuf);
    lim=chptr;
    while (chptrsave) {
	lim = scaddr(chptrsave);
	chptrsave = cadr(chptrsave);
    }
    if (lim<linebuf||linebuf+BUFSIZ<lim) {
	fprintf (stderr,"\n"); return;
    }
    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==RC) ? "'}'": (s==LC) ? "'{'": 
	  (s==COMMA) ? "','": 
	  (s==COLON) ? "':'": "Identifier";
	fprintf(stderr,"%d:%s expected.\n",lineno,p);
	errmsg();
    } 
    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));
	heap[0] = 0; // car(0) case
    }
    if (!heap) error(MMERR);
    gfree=1;
    labelno=2;
    set_lfree(heapsize);
}

void
set_lfree(int save)
{
    lfree = save;
    if (lfree_type_limit  && lfree_type_limit < lfree)
	error(-1);
}

/*
   Par file initialization.
 */

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

static void
reinit(void)
{
    int i;

    struct cheap *p;

    global_list = &null_nptr;
    cheap=cheap0;
    st_cheap = cheap1;
    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;
    free_glist2_list = 0;
    free_glist3_list = 0;
    parse = 0;
    inline_funcs = 0;
    inmode = 0;

    heap_init();

    reserve("int",INT,RESERVE);
    reserve("void",VOID,RESERVE);
    reserve("char",CHAR,RESERVE);
    reserve("const",KONST,RESERVE);
    reserve("__const__",KONST,RESERVE);
    reserve("struct",STRUCT,RESERVE);
    reserve("union",UNION,RESERVE);
    reserve("unsigned",UNSIGNED,RESERVE);
    reserve("signed",SIGNED,RESERVE);
    reserve("static",STATIC,RESERVE);
    reserve("goto",GOTO,RESERVE);
    reserve("return",RETURN,RESERVE);
    reserve("__return",RETURN,RESERVE);
    reserve("_CbC_return",RETURN,RESERVE);
    reserve("break",BREAK,RESERVE);
    reserve("continue",CONTINUE,RESERVE);
    reserve("if",IF,RESERVE);
    reserve("else",ELSE,RESERVE);
    reserve("for",FOR,RESERVE);
    reserve("do",DO,RESERVE);
    reserve("while",WHILE,RESERVE);
    reserve("switch",SWITCH,RESERVE);
    reserve("case",CASE,RESERVE);
    reserve("default",DEFAULT,RESERVE);
    reserve("typedef",TYPEDEF,RESERVE);
    reserve("sizeof",SIZEOF,RESERVE);
    reserve("long",LONG,RESERVE);
    reserve("short",SHORT,RESERVE);
    reserve("extern",EXTRN,RESERVE);
    reserve("defined",DEFINED,RESERVE);
    reserve("register",REGISTER,RESERVE);
#ifdef USE_CODE_KEYWORD
    reserve("code",CODE,RESERVE);
#endif
    reserve("__code",CODE,RESERVE);
    reserve("environment",ENVIRONMENT,RESERVE);
    reserve("_CbC_environment",ENVIRONMENT,RESERVE);
    reserve("float",FLOAT,RESERVE);
    reserve("double",DOUBLE,RESERVE);
    reserve("inline",INLINE,RESERVE);
    reserve("enum",ENUM,RESERVE);
    reserve("volatile",VOLATILE,RESERVE);
    reserve("__volatile__",VOLATILE,RESERVE);
    reserve("restrict",RESTRICT,RESERVE);
    reserve("typeof",TYPEOF,RESERVE);
    reserve("__typeof__",TYPEOF,RESERVE);
    reserve("__builtin_alloca",ALLOCA,RESERVE);
    reserve("__builtin_constant_p",BUILTINP,RESERVE);
    reserve("__builtin_expect",BUILTIN_EXPECT,RESERVE);
    reserve("__builtin_fabs",BUILTIN_FABS,RESERVE);
    reserve("__builtin_fabsf",BUILTIN_FABSF,RESERVE);
    reserve("__builtin_fabsl",BUILTIN_FABSL,RESERVE);
    reserve("__builtin_inf",BUILTIN_INF,RESERVE);
    reserve("__builtin_inff",BUILTIN_INFF,RESERVE);
    reserve("__builtin_infl",BUILTIN_INFL,RESERVE);
    reserve("__attribute__",ATTRIBUTE,RESERVE);
    reserve("__attribute",ATTRIBUTE,RESERVE);
    reserve("__label__",LABEL,RESERVE);
    reserve("__FILE__",C_FILE,RESERVE);
    reserve("__FUNCTION__",C_FUNCTION,RESERVE);
    reserve("__func__",C_FUNCTION,RESERVE);
    reserve("__LINE__",C_LINE,RESERVE);
#if ASM_CODE
    reserve("asm",ASM,RESERVE);
    reserve("__asm",ASM,RESERVE);  // ?
    reserve("__asm__",ASM,RESERVE);
#endif
    // attributes ( in different name space )
    reserve("aligned",ALIGNED,ATTRIBUTE);
    reserve("noreturn",NORETURN,ATTRIBUTE);

    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];i++) {
	if (av[i][0]=='-'&&av[i][1]=='D')
	    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);
    save_cheap(&scheap1,cheap); // make gcc happy

    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->ptr = 0; // by T.Matsu
	*cheap->ptr = *s; cheap = increment_cheap(cheap,&p); // by T.Matsu

    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);
    conv->open_(filep->name0);

    if(!ccout && !chk) {
	ccout = make_filename_with_extension(filep->name0,"s");
	if ( (freopen(ccout,"w",stdout)) == NULL ) error(FILERR);
	ccout=0;
    }
    opening(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 !UDPCL
    else if (macroeq("c2cbc")) conv=&c2cbc_converter;
    else if (macroeq("cbc2c")) conv=&cbc2c_converter;
    else if (macroeq("c")) conv=&c_converter;
#else
    else if (macroeq("udpcl")) conv=&udpcl_converter;
#endif
    else conv=&null_converter;
}

/*
    regist reserved word
 */

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

    if (d==0) error(-1);
    (nptr = name_space_search(get_name(s,0,DEF),0))->sc = sc;
    nptr->dsp = d;
}

/*
     Parse part
 */

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

static void
storage_class()
{
    qualifiers();
    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;
    }
    if(sym==INLINE) {
	getsym(0);
	inmode = INLINE;
    }
}

/*
       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;
    ctmode=0;
    if (mode==GDECL) { typedefed=0;  }
    storage_class();
    if((t=typespec())==0) return;
    if(sym==SM) {
	conv->return_type_(t,0,stypedecl);
	conv->sm_(); return;
    }
    storage_class();
    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;
	}
    }
    while (sym==ATTRIBUTE||sym==ASM) { 
	int sym0 = sym; getsym(0); attributes(sym0); 
    }
    if(sym==LC || ( sym!=SM && sym!=COMMA && sym!=ASS)) {
	/* function body */
	if (mode!=GDECL) {
	    error(DCERR);
	    return; // we cannot trust it's arguments
	}
        stypedecl=sd;
	if (type<0) error(DCERR);
	else if (car(type)==CODE) {
	    if (is_function(n)) error(UFERR);
	    code_decl(n); return;
	} else if (car(type)==FUNCTION) {
	    if (is_code(n)) error(UCERR);
	    fdecl(n); return;
	} else error(DCERR);
    } else {
	int init = 0;
	int v;
	conv->return_type_(type,n,sd);
	n = def(n,ctmode);
	v = list3n(n->sc,n->dsp,n);
	if (sym==ASS && n!=&null_nptr) { 
	    conv->op_(sym);
	    init = decl_data(type,v,0,0); data_closing(v); 
	}
	if (inmode && (mode==LDECL||mode==LLDECL||mode==STADECL)) { 
	    parse = list5n(ST_DECL,parse,list3(mode,stmode,ctmode),init,n);
	}
	while(sym==COMMA) {
	    init = 0;
	    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,ctmode);
	    v = list3n(n->sc,n->dsp,n);
	    if (sym==ASS && n!=&null_nptr) {
		conv->op_(sym);
		init = decl_data(type,v,0,0);data_closing(v);
	    }
	    if (inmode && mode==LDECL) {
		parse = list5n(ST_DECL,parse,
		    list3(mode,stmode,ctmode),init,n);
	    }
	}
	if(sym!=SM) error(DCERR);
	conv->sm_();
	if(mode==GTDECL)
	    mode=GDECL;
	if(mode==STADECL||mode==LTDECL)
	    mode=LDECL;
    }
}

static void
attributes(int attr)
{
    int smode = mode; mode=ATTRIBUTE;
    checksym(LPAR);
    if (attr==ASM) {
	//  int r __asm("r0") 
	getsym(ATTRIBUTE);
	if (sym==STRING) {
	    attribute = list3n(ASM,attribute,nptr);
	}
	mode = smode;
	checksym(RPAR);
	return;
    }
    while(sym!=RPAR) {
	if (sym==LPAR) {
	    attributes(0);
	} else if (sym==IDENT) {
	    attribute = list3n(IDENT,attribute,nptr);
	    getsym(ATTRIBUTE);
	} else if (sym==STRING) {
	    attribute = list3n(STRING,attribute,nptr);
	    getsym(ATTRIBUTE);
	} else {
	    attribute = list3(sym,attribute,0);
	    getsym(ATTRIBUTE);
	    if (sym==LPAR) {
		getsym(ATTRIBUTE);
		while(sym!=RPAR) {
		    caddr(attribute) = list3(sym,caddr(attribute),symval);
		    getsym(ATTRIBUTE);
		}
		getsym(ATTRIBUTE);
	    }
	}
    }
    mode = smode;
    getsym(0);
}

static void
qualifiers()
{
    for(;;) {
	switch (sym) {
	case KONST:
	    ctmode |= KONST_BIT;
	    break;
	case VOLATILE:
	    ctmode |= VOLATILE_BIT;
	    break;
	case RESTRICT:
	    ctmode |= RESTRICT_BIT;
	    break;
	case ATTRIBUTE:
	    getsym(0);
	    attributes(ATTRIBUTE);
	    continue;
	case INLINE:
	    inmode = INLINE;
	    getsym(0);
	    continue;
	default:
	    return;
	}
	getsym(0);
    }
    /* not reached */
}

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

    qualifiers();
    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);
	else if(sym==UNSIGNED) {
	    getsym(0); t = USHORT;
	}
	break;
    case LONG:
	t=INT;
	getsym(0);
	if(sym==LONG) {
	    getsym(0);
	    t=LONGLONG;
	    if (sym==INT) getsym(0);
	    else if (sym==UNSIGNED) { 
		if(getsym(0)==INT) getsym(0);
		t=ULONGLONG; break; 
	    } //
	} else if(sym==DOUBLE) {
	    getsym(0);
	    t=DOUBLE;
	} else if(sym==INT) { getsym(0);
	} else if(sym==UNSIGNED) { 
	  if(getsym(0)==INT) getsym(0);
	  t=UNSIGNED; 
	} //
	break;
    case TYPEOF:
	getsym(0);
	slfree=lfree; stype=type;
	smode = mode; mode = STAT;
	checksym(LPAR);
	mode = LDECL;  // typespec required this
	if((t=typespec())==0) {
	    mode = STAT;   // too late for expression 
	    expr(0); 
	    t = type;
	}
	set_lfree(slfree); type=stype;
	mode = smode;
	checksym(RPAR);
	return t;
	break;
    default:
	if(sym==IDENT) {
	    if (nptr->sc==TYPE) {
		t=nptr->ty;
		typedefed=glist3n(TYPEDEF,typedefed,nptr);
		// have to be type attribute
		copy_attributes(nptr);
		getsym(0);
		break;
	    } else if(nptr->sc==EMPTY && gnptr->sc==TYPE) {
		// ???
		getsym(0);
		break;
	    }
	}
	if(mode==LDECL) return 0;   // not a type
	t= INT;                     // empty typespec 
    }
    qualifiers();
    if (ctmode) {
	t = gset_type_attr(t,ctmode);
    }
    return t;
}

/*
      indirect *
      type prefix
 */
static struct nametable *
decl0(void)
{
    NMTBL *n;
    if(sym==MUL) {
	attribute = 0; // do not inherite type attributes
	getsym(0);
	qualifiers();
	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);
		type=list3(ARRAY,type,0);
	    } else {
		array_type=type;
		i=inmode?cexpr(pexpr(expr(1))):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) {
			int sargs=args;
			enter_scope();
			arg=adecl(n);
			leave_scope();
			args=sargs;
		    } else 
			arg=adecl(n);
		}
		type=glist3(CODE,CODE,arg);
	    } else {
		if(sym==RPAR) {
		    getsym(0);arg=0;
		} else {
		    if (mode==ADECL) {
			int sargs=args;
			enter_scope();
			arg=adecl(n);
			leave_scope();
			args=sargs;
		    } 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;
    int sctmode = ctmode;
    int sinmode = inmode;
    ctmode=0;
    if (parse_mode) inmode = INLINE;

    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,ctmode);
	    getsym(0);
	    if(sym==RPAR) break;
	} else {
	    if(sym==DOTS) {
		argtypes=list2(DOTS,argtypes);
		attribute = 0;
		getsym(0);
		break;
	    }
	    if((t=typespec())==0) {
		attribute = 0;
		error(DCERR);
		break;
	    }
	    type=t;
	    if(sym!=COMMA && sym!=RPAR) {
		sargs = args;
		arg=decl0();
		args = sargs;
		reverse(t); // this sets type also
		if (arg != &null_nptr) {
		    if (smode==GDECL) {
			def(arg,ctmode);
		    }
		}
	    }
	    attribute = 0;
	    argtypes=list2(type,argtypes);
	    if(sym==RPAR) break;
	}
	if (sym!=COMMA) error(DCERR);
	ctmode=0;
	getsym(0);
    }
    argtypes=reverse0(argtypes);
    n->dsp=reverse0(n->dsp);
    checksym(RPAR);
    mode=smode;
    fnptr=sfnptr;
    type=stype;
    sdecl_f = sd;
    ctmode = sctmode;
    stmode=sstmode;
    inmode = sinmode;
    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)
{
    t = type_value(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_pointer;
    case FUNCTION:
	return size_of_pointer;
    case POINTER:
	return size_of_pointer;
    default:
	error(DCERR);
    }
    return 0;
}

#define hash_value(hash,ch) (hash = (37*hash)^(11*(unsigned char)(ch)))
// #define hash_value(hash,ch) (hash = (37*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 int
decl_data_1(int type,int v,int offset)
{
    int t;

    // casted initializer    (sturct hoge){.a=3,.b=c,...}

    getsym(0);
    if (sym==LPAR) {
	offset = decl_data_1(type,v,offset);
	checksym(RPAR);
    } else if (typeid(sym)) {
	t = typename();
	checksym(RPAR);
	offset = decl_data(t,v,offset,1);
	if (inmode)
	    offset = list4(CAST,offset,t,type);
    } else {
	return 0;
    }
    return offset;
}

static int
decl_data_field(int type,int v,int offset)
{
    int t1,t2,period=0;
    int foffset;
    // int offset0 = offset+size(type);
    int mode_save=mode;
    int type0=type_value(type);
    NMTBL *nptr1;

    if(cadr(type0)==-1) {  // no struct field defenition
	error(DCERR);
	return offset;
    }
    t1 = caddr(type0);  /* list of fields */
    if (sym==LC) {
	mode = STAT;
	getsym(0);
    } else if (sym==LPAR) {
	// have to be a value, no comma cascading values
        //    .__tcp_lhash_lock = (rwlock_t) { },
        // We cannot distinguish this case and cascading comma here.
        // Do it more upper syntactical node;
	if (inmode) {
	    int offset1=decl_data_1(car(t1),v,0);
	    offset1 = reverse0(offset1);
	    offset=list3(DECL_DATA,offset1,car(t1));
	    return offset;
	}  else {
	    offset=decl_data_1(car(t1),v,offset);
	    if (!offset) checksym(RPAR);
	    return offset;
	}
    } else {
	error(SIERR);
    }
    mode=SFDINIT;
    while(1) {
	if (sym==PERIOD) { /* struct/union field initializaer */
	    period=1;
	    getsym(0);
	    if (sym==IDENT) {
		nptr1 = nptr;
		t2 = search_struct_type(type,nptr1->nm,&foffset);
		if (!t2) error(UFLDERR);
		getsym(0);
		if (sym==ASS) {
		    if (inmode) {
			int offset1 = decl_data(t2,v,0,0);
			offset = list4n(DECL_DATA_FIELD,offset,offset1,nptr1);
		    } else {
			decl_data(t2,v,offset+foffset,0);
		    }
		} else
		    error(INERR);
	    } else
		error(INERR);
	} else {
            if(period) {
		if (sym==RC) break;  /*  {.hoge=v,} case */
		error(INERR);
	    }
	    if(!t1) break; // empty field case (it can happen...)
	    // next decl_data must skip getsym
	    if (inmode) {
		int offset1 = decl_data(car(t1),v,0,1);  /* alignment? */
		offset = list4(DECL_DATA_LIST,offset,offset1,car(t1));
	    } else {
		offset = decl_data(car(t1),v,offset,1);  /* alignment? */
	    }
	    t1 = cadr(t1);
	}
	if ( t1 && sym==COMMA) { conv->comma_(); getsym(0); continue; }
	// if (!t1 && sym==COMMA) getsym(0); /* extra comma */
	if (sym==RC) break; // premature end
    }
    mode = mode_save;
    return offset;
}


#if LOCAL_STRUCT_INIT_STATIC 

// should be in mc-codegen.c

static void
local_struct_static(int v)
{
    NMTBL *nptr0,*n = ncaddr(v);
    int sz = size(type),offset=0;
    int smode = mode;
    int v0;
    // uninitialized part should be 0.
    // local var init cannot postponed because of assign_expr0/type
    //  if initialization contains expressions,
    //  we cannot do it in STADECL, but we can assign later in this mode
    if (inmode) error(-1);

    if (local_nptr)  error(-1);
    local_nptr = n;  // will be clear in flush

    mode=STADECL;
    nptr0=new_static_name("__lstruct",'_');
    def(nptr0,0);
    mode=smode;
    v0 = list3n(GVAR,0,nptr0);
    //nptr0->next = local_static_list; local_static_list = nptr0;
    //nptr0->sc = STATIC;
    //nptr0->ty = t = type;
    decl_data_field(type,v0,offset);
    // do struct assignment before flushed assignment expression
    init_vars = list2(
	list4(STASS,
	    inmode?(
		offset?
		    list3(ADD,list3n(IVAR,n->dsp,0),list2(CONST,offset)):
		    list3n(IVAR,n->dsp,0)):
		list3n(LVAR,n->dsp+offset,0),
	    list3(RSTRUCT,v0,sz),sz),
	init_vars);
    mode=STADECL;
    flush_delayed_decl_data(v);
    mode = smode;
}
#endif

/*
    data structure initialization

    int a = 1;
    int b[] = {1,2,3};
    struct s s = {1,2,{2,3}};
    struct s s = {.a=1,.b=2};
    struct s s = {.a=(struct b){1,2,.b=3}};
    
    list item
	list4(DECL_DATA_LIST,next,value,type);  // coarsed later
    list item with struct tag or array tag
	list4n(DECL_DATA_FIELD,next,decl_data,(NMTBL*)nptr);
    data initializer (array or struct)   // data type for field
	list3(DECL_DATA,value,type);

    cast があるかどうかの区別
 */

static int
decl_data(int t, int v,int offset,int skip)
{
    int t0,t1=0,e,i,mode_save,lc=0;
    int offset0;
    NMTBL *n = ncaddr(v);

    emit_init_vars();
    conv->decl_data_();
    t0 = type_value(t);
    mode_save = mode;
    mode=STAT;
    if (offset==0) {
	if (n->sc==GVAR && n->dsp==-1) {
	    /* duplicate initialization */
	    error(INERR);
	} else {
	    if (mode_save==GDECL && has_attr(n,KONST)) {
		mode=GDECL;
	    }
	}
    }
    if (!skip) getsym(0);
    if (sym==RC) {  /* premature end (not necessary?) */
	conv->decl_data_end_();
	mode = mode_save;
	return offset;
    } else if (scalar(t)) {
	if (sym==LC) { lc=1; getsym(0); }
 	e=expr1();
	if (lc) checksym(RC);
	mode = mode_save;
	if (inmode) {
	    offset = list3(DECL_DATA,e,type);
	    type=t;
	    return offset;
	}
 	//if(car(e)!=CONST && t==CHAR)
 	//    error(TYERR);
	// correct_type is too weak, should handle ADDRESS/ARRAY
	//   assign_data call rvalue, this is a duplicate rvalue call (correct?)
	if (!scalar(type) && (type>0 && (car(type)!=ADDRESS && car(type)!=ARRAY)))
	    e = correct_type(rvalue(e),t0);
 	offset = assign_data(e,t,v,offset);
 	type=t;
	return offset;
    } else if (t0==FLOAT||t0==DOUBLE||t0==LONGLONG||t0==ULONGLONG) {
	if (sym==LC) { lc=1; getsym(0); }
 	e=expr1();
	if (lc) checksym(RC);
	mode = mode_save;
	if (inmode) {
	    offset = list3(DECL_DATA,e,type);
	    type=t;
	    return offset;
	}
	e = correct_type(rvalue(e),t0);
 	offset = assign_data(e,t,v,offset);
 	type=t;
	return offset;
    } else if (t0>0 && (t1 = car(t0)) && t1==ARRAY) {
	if (sym==LC) {
	    int offset1 = 0;
	    //   int a[] = {... }
	    conv->lc_();
	    conv->decl_data_begin_();
	    mode = mode_save;
	    t1 = cadr(t);
	    i = 0;
	    for(;;) {
		if (sym!=RC) {
		    if (inmode) {
			int e=decl_data(t1,v,0,0); 
			offset1 = list4(DECL_DATA_ARRAY,offset1,e,t1);
		    } else {
			offset0 = offset;
			/* array of some thing */
			offset=decl_data(t1,v,offset,0); 
			if (offset0!=offset) i++;
		    }
		}
		if (sym==COMMA) {
		    conv->comma_();
		    continue;
		} else if (sym==RC) {
		    conv->decl_data_end_();
		    conv->rc_();
		    if (inmode) {
			getsym(0);
			offset1 = reverse0(offset1);
			offset = list3(DECL_DATA,offset1,t);
			return offset;
		    }
		    if (caddr(t)==0) {           /* size not defined      */
			caddr(t)=i;           /* define array size     */
		    } else if (caddr(t)!=i) {  /* size match?           */
			if (caddr(t) < i ) {   /* too many data */
			    // this check is sligtly odd (fix me)
			    // error(INERR);
			} else if (!decl_str_init)
			    assign_data(list2(CONST,caddr(t)-i),EMPTY,v,offset);
		    }
		    getsym(0);
		    return offset;
		}
	    }
	    /* NOT REACHED */
	} else if (type_value(cadr(t0))==CHAR) {
	    e=expr1();
	    mode = mode_save;
	    if(car(e)!=STRING)
		error(TYERR);
	    if (inmode) {
		offset = list3(DECL_DATA,e,type);
		return offset;
	    }
	    offset=assign_data(e,list3(ARRAY,CHAR,size(type)),v,offset);
	    if (caddr(t0)==0) {                  /* size not defined      */
		caddr(t0)=size(type);           /* define array size     */
	    } else if (caddr(t0)!=size(type)) {  /* size match?           */
		if (caddr(t0)>size(type)) {
		    // shorter string needs filler
		    return offset;
		}
		error(TYERR);
	    }
	    return offset; 
	}
    } else if (t1==BIT_FIELD) {
	/*
              Calculation of bitfiled in compile runtime is too difficult.
         */
	if(mode==GDECL) error(INERR); 
 	e=expr1();
	mode = mode_save;
	// e = correct_type(e,t);  correct me
	if (inmode) {
	    offset = list3(DECL_DATA,e,type);
	    type=t;
	    return offset;
	}
 	offset = assign_data(e,t,v,offset);
 	type=t;
	return offset;
    } else if (t1==STRUCT||t1==UNION) {
	//  struct hoge a = {...}
        if (sym==LC) lc=1; 
	conv->lc_(); conv->decl_data_begin_();
	mode = mode_save;
#if LOCAL_STRUCT_INIT_STATIC 
	if(mode==LDECL && !inmode) {
	    if (offset) error(-1);
	    local_struct_static(v); 
	    // change mode to STADECL
	    // decl_data_field(t,n,offset) is called inside;
	} else
#endif
	if (inmode) {
	    int offset1=decl_data_field(t,v,0);
	    offset1 = reverse0(offset1);
	    offset=list3(DECL_DATA,offset1,t);
	} else {
	    offset=decl_data_field(t,v,offset);
	}
	conv->decl_data_end_(); conv->rc_();
	if (lc) {
	    while (sym==COMMA) getsym(0);  // why we need this?!
	    checksym(RC);
	}
	return offset;
    } 
    mode = mode_save;
    error(TYERR); /* should be initialization error */
    return offset; /* not reached */
}

/*
    declaration in struct/union
 */
static void
sdecl_field(int smode)
{
    while (getsym(0) != RC) {
        decl();
    }
    if (sdecl_f) conv->rc_();
    mode = smode;
    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,mymode,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);
    mymode = mode;
    sdisp=disp;
    sbit_field_disp=bit_field_disp;
    disp=0;
    bit_field_disp=0;
    if (sdecl_f) conv->sdecl_(s);
    if (getsym(TAG) == IDENT) {
	/* struct tag name */
	nptr0 = nptr;
	gnptr0 = gnptr;
	if (sdecl_f) conv->id_(sym,nptr);
	mode = smode;  // return to (possibly) LDECL for next getsym
	if (getsym(0) == LC) {
	    /* struct define case */
	    mode=mymode;
	    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 = list4n(s,-1,0,nptr0);
	    sdecl_field(smode);
	    caddr(nptr0->ty)=fields;
	    cadr((type0 = nptr0->ty))=disp;
	    /* type0 = list4(s,disp,fields,0); now ... */
	} else {
	    /* struct reference case */
	    mode=mymode;
	    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 = glist4n(s,disp,fields,nptr0);
	}
    } else if(sym==LC) {
	/* no tag name */
	if (sdecl_f) conv->lc_();
	sdecl_field(smode);
	type0 = glist4n(s,disp,fields,0); // mode is now previous mode
    }
    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 = inmode?cexpr(pexpr(expr1())):cexpr(expr1());
	    }
	    nptr0->dsp = disp;
	    if (sym!=COMMA) break;
	    disp++;
	}
	checksym(RC);
    }
    type = ENUM;
    disp=sdisp;
    mode=smode;
    return type;
}

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

static void
local_decl(int scope)
{
    if (scope) 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_();
}

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

    if (parse_mode)  {
	sinmode = inmode;
	inmode = INLINE;
    }

    if (!inmode) {
	if (stmode==0 && n->sc==STATIC) {
	    // static proto type, no storage class in definition
	    stmode=n->sc;
	}
	if(!chk) gen_code_enter(n->nm);
	extrn_use(n);
    } else {
        if (parse && (car(parse)!=ST_DECL&&car(parse)!=ST_COMMENT))
            error(-1);  // What kind of error?
        if (parse && car(parse)==ST_COMMENT)
            cadr(parse)=0;
        else
            parse = 0;
    }
    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);
    }
    if (!inmode) {
	/* reverse all argument offset (with size) */
	arglist = fnptr->dsp;
	for(t=arglist;t;t=cadr(t)) {
	    n=ncadddr(t);
	    if(n->sc==LVAR)
		n->dsp = -n->dsp-caddr(t);
	}
	arg_register(fnptr);
    }
    arg_disp = args;

    typedefed=0;
    conv->code_(fnptr); conv->lc_();
    /* local variable declaration */
    control=1;
    cslabel = -1;
    local_decl(0);
    if(!inmode && !chk) gen_code_enter1(args);
    emit_init_vars();
    // lfree_type_limit = lfree;
    while(sym!=RC) statement(0);
    if(control)
	error(CODE_ERR);
    control=0;
    conv->code_end_();
    if (inmode) {
        set_attr(n,INLINE,
            list4n(reverse0(parse),arg_disp,disp,local_static_list));
	if (parse_mode && ! sinmode) {
	    set_attr(n,NOINLINE,-1);
	}
        parse = 0;
        inline_funcs = list3n(INLINE,inline_funcs,n);
    } else {
	if(!chk) gen_code_leave(fnptr->nm);
    }

    args = 0;
    // lfree_type_limit = 0;
    if (parse_mode) {
	// do not generate static inline function
	if (sinmode && n->sc==STATIC) return;
	inmode = 0;
	pcode_decl(fnptr);
	inmode = 0;
    }
}

static NMTBL *tmp_struct;

/* function define */


static void
fdecl(NMTBL *n)
{
    int sd = stypedecl;
    int arglist,arg_disp;
    int sinmode = inmode;
    if (parse_mode)  {
	inmode = INLINE;
    }
    if (!inmode) {
	if (stmode==0 && n->sc==STATIC) {
	    // static proto type, no storage class in definition
	    stmode=n->sc;
	}
	if(!chk) gen_enter(n->nm);
	extrn_use(n);
	retlabel=fwdlabel();
    } else {
	if (parse && (car(parse)!=ST_DECL&&car(parse)!=ST_COMMENT))
	    error(-1);  // What kind of error?
	if (parse && car(parse)==ST_COMMENT)
	    cadr(parse)=0;
	else
	    parse = 0;
    }
    local_static_list = &null_nptr;
    fnptr=n;
    retcont = 0;
    // if (tmp_struct) { tmp_struct->sc = 0; tmp_struct->nm = 0; } // why?
    tmp_struct = 0; /* a = f().field  */

    n->ty = type;
    fcheck(n);
    // n->sc = FUNCTION;  // static information?
    mode=ADECL;
    if (sym!=LC) {
	arglist = fnptr->dsp;
	fnptr->dsp =args=0; 
	while (sym!=LC) { /* K&R sytle argument declaration */
	    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 */
    arg_disp = args;
    args = 0;
    disp=0;
    if (!inmode)
	arg_register(fnptr);
    typedefed=0;
    conv->function_(fnptr,sd); conv->lc_();

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

    conv->function_end_(); conv->rc_();
    if (inmode) {
	set_attr(n,INLINE,
	    list4n(reverse0(parse),arg_disp,disp,local_static_list)); 
	if (parse_mode && ! sinmode) {
	    set_attr(n,NOINLINE,-1);
	}
	parse = 0;
	inline_funcs = list3n(INLINE,inline_funcs,n);
	inmode = 0;
    } else {
#if 0
	if (type_value(cadr(fnptr->ty))!=VOID) {
	    if (control && !retpending && !pending_jmp) 
		error(ENERR); // function has return value but reached to the end
	}
#endif
	if(!chk) gen_leave(control,n->nm);
    }
    retpending = 0;
    control=0;
    arglist=0;
    lfree_type_limit  = 0;
    if (parse_mode) {
	// do not generate static inline function
	if (sinmode && n->sc==STATIC) return;
	inmode = 0;
	pfdecl(fnptr);
	inmode = 0;
    }
}

/* copy function argment for inline function */
/*     calcurate argument offset here */

static int
copy_arg(int arg) 
{
    NMTBL *a,*n1;
    int offset=0;
    int nargs;

    for(nargs=0;arg;arg=cadr(arg)) {
	int t; 
	int sz ;

	n1 = get_nptr();
	a = ncadddr(arg);
	n1->ty = a->ty;
	n1->nm = a->nm;
	n1->sc = a->sc==IVAR?LVAR:a->sc;
	n1->attr = a->attr;

	t = type_value(n1->ty);
	sz = size(t);
	offset = arg_alignment(offset,n1,t,sz);

	nargs=list4n(car(arg),nargs,caddr(arg),n1);
    }
    args = offset;
    return nargs;
}

/* generate function from parse tree */
/*    some inline functions are external or indirectly called */
/*    even if it is a static. Generate these in closing();    */
/* should be in mc-inline.c? */

extern void
pfdecl(NMTBL *n)
{
    int e;
    int args,nargs,cargs;
    NMTBL *a;

    if (has_attr(n,GENERATED)) return;
    set_attr(n,GENERATED,0);

    top_init();

    stmode = STATIC;
    if (is_code(n)||is_function(n)) {
	if (n->sc==EXTRN1||n->sc==EXTRN) {
	    stmode = EXTRN;
	}
    }

    if(!chk) gen_enter(n->nm);
    extrn_use(n);
    local_static_list = &null_nptr;
    retlabel=fwdlabel();

    fnptr=get_nptr();
    fnptr->ty=n->ty;
    fnptr->nm=n->nm;
    fnptr->sc=n->sc;
    fnptr->attr=n->attr;
    fnptr->next=0;
    
    // make copied called function argment
    nargs = copy_arg(n->dsp);

    // fdecl_struct(fnptr->ty);  already done by fdecl before
    fnptr->dsp=reverse0(nargs);
    
    retcont = 0;
    tmp_struct = 0;

    disp=0;
    arg_register(fnptr); // should fix n1->dsp

    // make calling argments
    for(args=fnptr->dsp,cargs=0;args;args=cadr(args)) {
	a = ncadddr(args);
	// make call function argment for gen_inline
        e=list3n(a->sc==IVAR?LVAR:a->sc,a->dsp,a);
	cargs = list3(e,cargs,a->ty);
    }
    //  build inline function call
    e = list4(INLINE,list3n(FNAME,0,n),cargs,
	    list3(car(fnptr->ty),cadr(fnptr->ty),caddr(fnptr->ty)));

    typedefed=0;
    init_vars=0;

    if(!chk) gen_enter1();

    control=1;
    cslabel = -1;

    mode = STAT;
    if(!chk) gen_inline(e,1);
    if(!chk) gen_leave(control,n->nm);

    retpending = 0;
    control=0;
}

extern void
pcode_decl(NMTBL *n)
{
    int e;
    int arg,nargs,cargs,t;
    NMTBL *a,*n1;

    if (has_attr(n,GENERATED)) return;
    set_attr(n,GENERATED,0);

    top_init();

    stmode = STATIC;
    if (is_code(n)||is_function(n)) {
	if (n->sc==EXTRN1||n->sc==EXTRN) {
	    stmode = EXTRN;
	}
    }

    if(!chk) gen_code_enter(n->nm);
    extrn_use(n);
    local_static_list = &null_nptr;
    retlabel=fwdlabel();

    fnptr=get_nptr();
    fnptr->ty=n->ty;
    fnptr->nm=n->nm;
    fnptr->sc=n->sc;
    fnptr->attr=n->attr;
    fnptr->next=0;
    
    // make copied called function argment
    nargs = copy_arg(n->dsp);

    // fdecl_struct(fnptr->ty);  already done by fdecl before
    fnptr->dsp=reverse0(nargs);

    /* reverse all argument offset (with size) */
    arg = fnptr->dsp;
    for(t=arg;t;t=cadr(t)) {
	n1=ncadddr(t);
	if(n1->sc==LVAR)
	    n1->dsp = -n1->dsp-caddr(t);
    }
    
    retcont = 0;
    tmp_struct = 0;

    disp = -args;
    arg_register(fnptr); // should fix n1->dsp

    // make calling argments
    for(arg=fnptr->dsp,cargs=0;arg;arg=cadr(arg)) {
	a = ncadddr(arg);
	// make call function argment for gen_inline
        e=list3n(a->sc==IVAR?LVAR:a->sc,a->dsp,a);
	cargs = list3(e,cargs,a->ty);
    }
    //  build inline function call
    e = list4(INLINE,list3n(FNAME,0,n),cargs,
	    list3(car(fnptr->ty),cadr(fnptr->ty),caddr(fnptr->ty)));

    typedefed=0;
    init_vars=0;

    if(!chk) gen_code_enter1(args);

    control=1;
    cslabel = -1;

    mode = STAT;
    if(!chk) gen_inline(e,1);
    if(!chk) gen_code_leave(n->nm);

    retpending = 0;
    control=0;
}

/*
    basic C statement
 */

static void
statement(int use)
{
    int slfree,e;
loop:
    if(sym==SM) {
	conv->sm_();
	getsym(0); return;
    } else if (typeid(sym) || sym==REGISTER ) {
	// anytime local declaration...
	mode=LDECL;
	stmode=0;
	lfree_type_limit = lfree;
	decl();
	mode=STAT;
	checkret();
	emit_init_vars();
	goto loop;
    }
    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) {
	    if (inmode) {
		parse = list2(ST_BREAK,parse);
	    } else {
		gen_jmp(blabel);
	    }
	}
	getsym(0);
	checksym(SM);
	return;
    case CONTINUE:
	checkret();
	conv->continue_();
	if (inmode) {
	    parse = list2(ST_CONTINUE,parse);
	} else if (control) gen_jmp(clabel);
	getsym(0);
	checksym(SM);
	return;
    case CASE:
	docase(); goto loop;
    case DEFAULT:
	dodefault(); goto loop;
    case RETURN:
	doreturn();
	return;
    case GOTO:
	dogoto();
	return;
#if ASM_CODE
    case ASM:
	doasm();
	return;
#endif
    default:
	checkret();
	if(sym==IDENT&&skipspc()==':') {
	    dolabel();
	    if (sym==RC) {
		fprintf(stderr," error: label at end of compound statement\n");
		return;
	    }
	    goto loop;
	} else {
	    //  ({... ; value}) requires delayed handling of lastexp.
	    //  lastexp will be flushed in checkret(), or used by
	    //  statement expression.
	    if (use) {
		lastexp = expr(0);
	    } else if (inmode) {
		e = expr(0);
		parse = list3(ST_COMP,parse,e);
	    } else {
		slfree=lfree;
		gexpr(expr(0),use);
		set_lfree(slfree);
	    }
	    conv->sm_();
	    checksym(SM);
	}
    }
}

static void
doif(void)
{
    int l1,l2=0,slfree,pparse=0;
    int if0;

    getsym(0);
    checksym(LPAR);
    conv->if_();
    slfree=lfree;
    checkret();
    if (inmode) {
	pparse = parse; parse = 0;
	l1 = if0 = expr(0);
    } else {
	l1 = bexpr((if0 = expr(0)),0,fwdlabel());
    }
    set_lfree(slfree);
    conv->if_then_(if0);
    checksym(RPAR);
    statement(0);
    checkret();
    if (inmode) {
	l2 = reverse0(parse); parse = 0;
    }
    if(sym==ELSE) {
	conv->if_else_();
	if (inmode) {
	    getsym(0);
	    statement(0);
	    checkret();
	    parse = list3(ST_IF,pparse,list3(l1,l2,reverse0(parse)));
	} else {
	    if ((l2 = control))
		gen_jmp(l2=fwdlabel());
	    fwddef(l1);
	    getsym(0);
	    statement(0);
	    checkret();
	    if (l2) fwddef(l2);
	}
    } else {
	if (inmode) {
	    parse = list3(ST_IF,pparse,list3(l1,l2,0));
	} else {
	    fwddef(l1);
	}
    }
    conv->if_endif_();
}

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

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

static void
dodo(void)
{
    int sbreak,scontinue,l=0,slfree,pparse=0,e;

    sbreak=blabel;
    scontinue=clabel;
    if (inmode) {
	checkret();
	pparse = parse; parse = 0;
    } else {
	blabel=fwdlabel();
	clabel=fwdlabel();
	control=1;
	checkret();
	l=backdef();
    }
    conv->dowhile_();
    getsym(0);
    statement(0);
    checkret();
    if (inmode) {
	l = reverse0(parse); parse =0;
    } else {
	fwddef(clabel);
    }
    checksym(WHILE);
    checksym(LPAR);
    slfree=lfree;
    conv->dowhile_cond_();
    if (inmode) {
	e = expr(0);
	parse = list4(ST_DO,pparse,e,l);
    } else {
	bexpr((e=expr(0)),1,l);
    }
    conv->dowhile_end_(e);
    set_lfree(slfree);
    checksym(RPAR);
    checksym(SM);
    if (!inmode)
	fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

static void
dofor(void)
{
    int pparse=0,p0=0,p1=0;
    int l=0,e=0,slfree,dflag=0;
    int sbreak=blabel;
    int slimit = lfree_type_limit;
    int sinit_vars = init_vars; 
    int scontinue=clabel;
    init_vars = 0;

    if (inmode) {
	pparse = parse; parse=0;
    } else {
	blabel=fwdlabel();
    }
    conv->for_();
    getsym(0);
    checksym(LPAR);
    slfree=lfree;
    //
    //   for(int hge;hge<0;hge--) {... }
    //
    if (typeid(sym) || sym==REGISTER ) {
	// initializer with declaration
	//      for has strange scope rule
	enter_scope(); dflag = 1;
	mode=LDECL;
	stmode=0;
	lfree_type_limit = lfree;
	decl();
	mode=STAT;
	checkret();
	if (chk) {   // why? kono
	    p0 = list3(ST_COMP,reverse0(init_vars),0);
	} else {
	    emit_init_vars();
	}
	if (inmode) {
	    p0 = reverse0(parse); parse = 0;
	}
	getsym(0);
    } else if(sym!=SM) {
	// initial expression (without declartion)
	checkret();
	if (inmode) {
	    p0 = expr(0);
	} else {
	    gexpr(expr(0),0);
	}
	checksym(SM);
	conv->for1_();
    } else {
	p0 = 0;
	conv->for1_();
	getsym(0);
    }
    set_lfree(slfree);
    control=1;
    checkret();
    if (!inmode) {
	l=backdef();
    }
    if(sym!=SM) {
	//  loop condition expression
	if (inmode) {
	    p1 = expr(0);
	} else {
	    bexpr((p1=expr(0)),0,blabel);
	}
	checksym(SM);
	conv->for2_(p1);
    } else {
	conv->for2_(0);
	p1 = 0;
	getsym(0);
    }
    set_lfree(slfree);
    e = 0;
    if(sym==RPAR) {
	//  no continue expression
	clabel=l;
	conv->for_body_(0);
	getsym(0);
	statement(0);
	checkret();
    } else {
	// continue expression
	clabel=fwdlabel();
	e=expr(0);
	conv->for_body_(e);
	checksym(RPAR);
	statement(0);
	checkret();
	if (!inmode) {
	    fwddef(clabel);
	    gexpr(e,0);
	}
	set_lfree(slfree);
    }
    conv->for_end_(p0,p1,e);
    if (dflag) leave_scope();
    if (inmode) {
	parse = list3(ST_FOR,pparse,list4(p0,p1,e,reverse0(parse)));
	// parse = list3(ST_FOR,pparse,list4(p0,p1,e,parse));
    } else {
	gen_jmp(l);
	fwddef(blabel);
    }
    lfree_type_limit = slimit ;
    clabel=scontinue;
    blabel=sbreak;
    init_vars=sinit_vars;
}

/*
    compound statement {}
	may contain declaration
 */
static void
docomp(int use)
{
    int slimit = lfree_type_limit ;
    int sinit_vars = init_vars;
    conv->lc_();
    local_decl(1);
    emit_init_vars();
    lfree_type_limit = lfree;
    while(sym!=RC) statement(use); 
    conv->rc_();
    lfree_type_limit = slimit; 
    init_vars = sinit_vars;
    leave_scope();
    getsym(0);
}

/*
     CASE_CODE generates table jump

    CASCADE
	switch(c)  {     compute c
                         jmp cslabel0
        case 5;     cslabel0:  cmp c,5
                               jne cslabel1
              .....
          break;               jmp blabel
        default:    dlabel:
        }           clsabel1:  if (defalut) jmp dlabel
                    blabel:
     CASE_CODE
	switch(c)  {     compute c
        case 5;     cslabel0:  cmp c,5
                               jne jtable_JMP  <-- at least one case required
              .....
          break;               jmp blabel
        default:    dlabel:
        }           clsabel1:  if (defalut) jmp dlabel
                    table_JMP:  table_jmp
                    blabel:

     cslist keeps list of case/label pair
 */

static void
doswitch(void)
{
    int sbreak=0,scase=0,sdefault=0,slfree=0,svalue=0,slist=0;
    int pparse = parse,v,cst=0;

    checkret();
    if (inmode) {
	parse = 0;
    }
    if (!inmode) {
	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 */
	v = expr(0);
	if (!scalar(type)) error(EXERR);
	if (car(v)!=CONST)
	    gexpr(v,1);
    } else {
	conv->switch_();
	getsym(0);
	checksym(LPAR);
	v = expr(0);
	if (!scalar(type)) error(EXERR);
    }
    cst = (car(v)==CONST);
    if (!inmode) {
	csvalue1=cst ? glist2(CONST,cadr(v)): csvalue() ;
	set_lfree(slfree);
	checksym(RPAR);
	conv->switch_body_(v);
	if (cst) {
	    gen_jmp( cslabel = fwdlabel());
	} else {
	    cslabel = control = 0;
	}
    } else {
	conv->switch_body_(v);
	checksym(RPAR);
    }
    /* 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_(cslist);
    checkret();
    if (inmode) {
	parse = list4(ST_SWITCH,pparse,v,reverse0(parse));
    } else {
#if CASE_CODE
	if (!cst) {
	    if (control) gen_jmp(blabel);
	    genswitch(cslist,cslabel);
	} else if (!cslist) {
	    if(dlabel) {
		df_label(cslabel,dlabel);
		cslist=1;
	    } else {
	    // no matched value, but some statement may haave control
	    if(pending_jmp!=cslabel)
		fwddef(cslabel);
	    else pending_jmp=0; // cslabel is here
	    }
	}
#else
	if (!(cst && cslit)) {
	    if(dlabel) df_label(cslabel,dlabel);
	    else fwddef(cslabel);
	}
#endif
	free_glist2(csvalue1);
    }
    if (!inmode) {
	fwddef(blabel);
	csvalue1=svalue;
	cslabel=scase;
	dlabel=sdefault;
	blabel=sbreak;
	cslist = slist;
    }
}

#if CASE_CODE
/* used in insert ascend */
extern int
docase_eq()
{
    error(-1);  // duplicate case value
    return 0;   // remove duplicate value
}
#endif

static void
docase(void)
{
#if CASE_CODE
    int l=0,clist=0,c,cst = (csvalue1 && car(csvalue1)==CONST);

    // if (cst) clist means there is a matching constant

    if (!inmode && !cst)
	l = fwdlabel();
    while(sym==CASE) {
	conv->case_begin_(0,0);
	getsym(0);
	// we have to make it global?
	c = inmode?cexpr(pexpr(expr1())):cexpr(expr1());
	if (!inmode && cst) {
	    clist |= (cadr(csvalue1)==c);
	    conv->case_(list2(c,0),0);
	} else {
	    clist=glist3(c,clist,inmode?0:l);
	    conv->case_(clist,0);
	}
	checksym(COLON);
    }
    if (cst && !clist) return;
    if (inmode) {
	parse = glist3(ST_CASE,parse,clist);
	control=1;
	return;
    }
    if (retpending) {
	ret(); retpending=0;
    }
    if (cst) {
	if (cslist) {
	    // error(CSERR);
	    return; // duplicate value
	}
	cslist=1;
	fwddef(cslabel);
	return;
    }
    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,clist,l,slfree;
    int cst = (csvalue1 && car(csvalue1)==CONST);
    if (!inmode && !cst) {
	if (retpending) { 
	    ret(); retpending=0;
	}
    } else {
	l = 0;
	slfree=lfree;
	c=0;
    }
    while(sym==CASE) {
	conv->case_begin_(c,0);
	getsym(0);
	c = inmode?cexpr(pexpr(expr1(1))):cexpr(expr1(1));
	if (!inmode && cst)
	    clist |= (c==cadr(csvalue1));
	else
	    clist=list2(c,clist);
	conv->case_(clist,0);
	checksym(COLON);
    }
    if (inmode) {
	parse = list3(ST_CASE,parse,clist);
	control=1;
	return;
    } 
    if (cst && ! clist) {
	set_lfree(slfree);
	return;
    }
    if (cst) {
	if (!cslist) {
	    if (retpending) { 
		ret(); retpending=0;
	    }
	    fwddef(cslabel);
	    cslist=1;
	}
	set_lfree(slfree);
	return;
    }
    if (retpending) { 
	ret(); retpending=0;
    }
    l=fwdlabel();
    if (control) {
	control=0;
	gen_jmp(l);
    }
    if (cslabel) fwddef(cslabel);
    while(cadr(clist)) {
	cmpdimm(car(clist),csvalue1,l,0);
	clist=cadr(clist);
    }
    cmpdimm(car(clist),csvalue1,cslabel=fwdlabel(),1);
    if (l) fwddef(l);
    set_lfree(slfree);
    /* control==1? */
#endif
}

static void
dodefault(void)
{
    // int cst = (car(csvalue1)==CONST); we cannot ommit this...
    control=1;
    checkret();
    getsym(0);
    checksym(COLON);
    if (inmode) {
	parse = list2(ST_DEFAULT,parse);
    } else {
	if (dlabel) error(STERR);  // double default:
	dlabel = backdef();
    }
    conv->case_(0,1);
}

// return statement
//     return values in a fixed register (architecture dependent)
//     struct value is copied to the place passed by the caller,
//     by called function, and return it's pointer to the caller.
//
//     current register is moved to corret one in checkret();
//     other complex stack work will be done at the last of
//     generated function. (retpending flag)

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

    if (is_code(fnptr)) {
	error(RETERR);
    }
    if (!inmode && !cslabel) gen_jmp(cslabel = fwdlabel());
    if(getsym(0)==SM) {
	// should check fnptr have no return value
	conv->return_();
	conv->return_end_(0);
	getsym(0);
	if (inmode)
	    parse = list3(ST_RETURN,parse,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) {
		/* CASCADING struct return */
		/* return struct_return_f(); case */
		/* pass the return pointer to the called function */
		replace_return_struct(cadr(e),
		    rvalue_t(car(struct_return),caddr(struct_return)));
		if (inmode) {
		    parse = list3(ST_RETURN,parse,cadr(e));
		} else
		    gexpr(cadr(e),0);
	    } else {
		type = caddr(struct_return);
		// e1 = rvalue_t(cadr(struct_return),INT); /* size */
		e1 = cadr(struct_return); /* size */
		if (inmode) {
		    parse = list3(ST_RETURN,parse,
			    list4(STASS,rvalue(car(struct_return)),e,e1));
		} else
		    gexpr(list4(STASS,rvalue(car(struct_return)),e,e1),0);
	    }
	} else {
	    error(TYERR); /* should check compatible */
	}
    } else {
	// normal value
	if (inmode) {
	    e = correct_type(expr(0),cadr(fnptr->ty));
	    parse = list3(ST_RETURN,parse,e);
	} else {
	    gexpr(correct_type((e=expr(0)),cadr(fnptr->ty)),1);
	}
    }
    conv->return_end_(e);
    set_lfree(slfree);

    checksym(SM);
    /* control = 0; still control continue until pending return emission */
    retpending = 1;
}

/*   CbC continuation */
static void
dojump(int e1,int env)
{
    int e2 = cadr(e1);
    NMTBL *nptr0;
    conv->jump_(env);
    if (car(e2) == FNAME) {
	nptr0=ncaddr(e2);
	if (nptr0->sc==EMPTY)
	    nptr0->sc = EXTRN1;
	else if(nptr0->sc==FUNCTION)
	    nptr0->sc = CODE;
	if (nptr0->ty>0&&car(nptr0->ty)==FUNCTION)
	    error(GTERR);
	    // car(nptr0->ty)=CODE;
    }
    if (inmode) {
	parse = list3(ST_GOTO,parse,list3(JUMP,e1,env));
    } else {
	gexpr(list3(JUMP,e1,env),0);
    }
    control=0;
    conv->sm_();
    checksym(SM);
    return;
}

static void
dogoto(void)
{
    NMTBL *nptr0;
    int t,e1,env;
    int sstmode=stmode;

    checkret();
    conv->goto_();
    stmode=GOTO;
    getsym(0);
    e1 = expr(0);
    stmode=sstmode;
    t=car(e1);
    if (type==VOID) {
	/* indirect goto */
	if (car(e1)==RINDIRECT) {
	    if (inmode) {
		parse = list3(ST_GOTO,parse,e1);
	    } else {
		gen_indirect_goto(cadr(e1));
	    }
	} else error(TYERR);
	checksym(SM);
	return;
    }
    if (t==FNAME) {
	/* classical goto */
	nptr0 = ncaddr(e1);
	t = nptr0->sc;
	if (t==EMPTY||t==EXTRN1||t==EXTRN) {
	    // error check?
	    nptr0->sc=EMPTY;
	    nptr0=l_top_search(nptr0->nm,0);
	    nptr0->sc = FLABEL;
	    if (!inmode) nptr0->dsp = fwdlabel();
	    else nptr0->dsp = --disp;
	} else if (!(t==FLABEL||t==BLABEL)) {
	    error(STERR);
	}
	if (!inmode) gen_jmp(nptr0->dsp);
	else parse = list3(ST_GOTO,parse,list3n(IVAR,nptr0->dsp,nptr0));
	control=0;
	conv->sm_();
	checksym(SM);
	conv->goto_label_(nptr0);
	return;
    }
    if (t==COMMA) {
	error(STERR);
	/* CbC environment option */
	env = caddr(e1);
	e1  = cadr(e1);
	t   = car(e1);
    } else {
	env = 0;
    }
    if (t==CODE) {
        /*   CbC continuation */
	dojump(e1,env);
	return;
    }
    error(STERR);
    return;
}

static void
dolabel(void)
{
    NMTBL *nptr1;
    control=1;
    checkret();
    if(nptr->sc == FLABEL) {
	// already used by goto with fwdlabel
	// or this is a local label defined by __label__
	if (!inmode) fwddef(nptr->dsp);
	else parse = list3(ST_LABEL,parse,list3n(IVAR,nptr->dsp,nptr));
    } else if(nptr->sc != EMPTY && nptr->sc != EXTRN1) {
	error(TYERR); // duplicate label
    } else {
	nptr->sc=EMPTY;
	// define this label in top level scope
	nptr1=l_top_search(nptr->nm,0);
	nptr1->sc = BLABEL;
	if (!inmode) nptr1->dsp = backdef();
	else {
	    nptr1->dsp = --disp;
	    parse = list3(ST_LABEL,parse,list3n(IVAR,nptr1->dsp,nptr1));
	}
    }
    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);
    qualifiers();
    checksym(LPAR);
    // asm string
    if (sym!=STRING) error(DCERR);
    asm0=list3n(STRING,nptr->dsp,nptr);
    getsym(0);
    if (sym!=COLON) {
#if 0
	if (sym==RPAR) {
	    // old type asm statement
	    goto output;
	}
#endif
	error(DCERR);
    }
    do {
	// output expression
	getsym(0);
	if (sym==COLON) break;
	if (sym!=STRING) error(DCERR);
	out=list2(list3n(STRING,nptr->dsp,nptr),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(list3n(STRING,nptr->dsp,nptr),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(list3n(STRING,nptr->dsp,nptr),opt);
	    getsym(0);
	} while(sym==COMMA);
    }
    checksym(RPAR);
    input = reverse0(input);
    out = reverse0(out);
    if (inmode) {
	parse = list4(ST_ASM,parse,list4(asm0,input,out,opt),e1);
    } else {
	gexpr(list3(ASM,list4(asm0,input,out,opt),e1),0);
    }
    checksym(SM);
}
#endif


/* C expression */

extern int
expr(int noconv)      // noconv=1 suppress convsion output
{
    int r;
    conv->noconv_(noconv);
    r=rvalue(expr0());
    if (noconv) conv->conv_();
    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,t;
    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_pointer ));
    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 RSTRUCT:
	    e=cadr(e);
	case DREGISTER:  /* should be error? */
	case FREGISTER:
	case LREGISTER:
	case REGISTER:
	case GVAR:
	case LVAR:
	case ARRAY:
	case ARROW:
	case PERIOD:
	    e=list2(ADDRESS,e);
	    break;
	case IVAR:
	    if ((nptr1=ncaddr(e))) {
		set_attr(nptr1,HAS_ADDRESS,1);
	    }
	    e=list2(ADDRESS,e);
	    break;
	case FNAME:
	    break;
	case CAST:
	    if (car(cadr(e))== DECL_DATA) break; 
	default:error(LVERR);
	}
	type=list2(POINTER,type);
	return e;
    case ADD:  /* +p */
	conv->prefix_(sym);
	getsym(0);
	return expr13();
    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);
	getsym(0);
	checksym(LPAR);
	e=rvalue(expr0());
	checksym(RPAR);
	type=list2(POINTER,VOID);
	return list2(ALLOCA,e);
    case BUILTINP:
	/* __builtin_constant_p GNU extenstion */
	conv->prefix_(sym);
	getsym(0);
	checksym(LPAR);
	e=expr0();
	checksym(RPAR);
	type=INT;
	if (inmode)
	    return list2(BUILTINP,rvalue(e));  /* evalue it later */
	else 
	    return list2(CONST,is_const(e));
    case BUILTIN_EXPECT:
	/* builtin_expect(x,c) used in branch. x is expectet to be c */
	conv->prefix_(sym);
	getsym(0);
	checksym(LPAR);
	e=expr1();
	t=type;
	checksym(COMMA);
	expr0();  /* ingore */
	checksym(RPAR);
	type=t;
	return e;
    case BUILTIN_FABSF:
	conv->prefix_(sym);
	getsym(0);
	checksym(LPAR);
	e=expr0();
	checksym(RPAR);
	e = list2(op,rvalue_t(e,FLOAT));
	type = FLOAT;
	if (is_const(e)) {
#if FLOAT_CODE
	    return (dcadr(e)>0.0) ? dcadr(e) : dlist2(FCONST,-dcadr(e));
#endif
	}
	return e;
    case BUILTIN_FABS:
    case BUILTIN_FABSL:
	conv->prefix_(sym);
	getsym(0);
	checksym(LPAR);
	e=expr0();
	checksym(RPAR);
	e = list2(op,rvalue_t(e,DOUBLE));
	type = DOUBLE;
	if (is_const(e)) {
#if FLOAT_CODE
	    return (dcadr(e)>0.0) ? dcadr(e) : dlist2(DCONST,-dcadr(e));
#endif
	}
	return e;
    case BUILTIN_INFF:
	conv->prefix_(sym);
	getsym(0);
	checksym(LPAR);
	checksym(RPAR);
	type = FLOAT;
	e = list2(op,0);
	return e;
    case BUILTIN_INF:
    case BUILTIN_INFL:
	conv->prefix_(sym);
	getsym(0);
	checksym(LPAR);
	checksym(RPAR);
	type = DOUBLE;
	e = list2(op,0);
	return e;
    case SIZEOF:
	conv->prefix_(sym);
	if(getsym(0)==LPAR) {
	    conv->lpar_();
	    if(typeid(getsym(0))) {
		e=list2(CONST,size(t=typename()));
		type=INT;
		checksym(RPAR);
		conv->type_(t);
		conv->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);
		    }
		}
		conv->rpar_();
	    }
	} else
	    expr13();
	e=list2(CONST,size(type));
	type=INT;
	return e;
    case LAND: /* &&p  gcc extension label value */
	getsym(0);
	if (sym!=IDENT) error(TYERR);
#if 0
	e = expr13();
	type = car(e);
	if (type!=FNAME) {
	    error(TYERR); 
	}
        nptr1 = ncaddr(e);
#else
	nptr1 = nptr;
	getsym(0);
#endif
        type = nptr1->sc;
        if (type==EMPTY||type==EXTRN1||type==EXTRN) {
            nptr1->sc=EMPTY;
            nptr1=l_top_search(nptr->nm,0);
            nptr1->sc = FLABEL;
	    if (inmode!=INLINE)
		nptr1->dsp = fwdlabel();
	    else
		nptr1->dsp = --disp;
        }
	type = list2(POINTER,VOID);
	// can be global?!
	return list2(LABEL,
	    list3n(inmode==INLINE?IVAR:LVAR,nptr1->dsp,nptr1));
    }
    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_pointer ));
    }
    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=list3n(FNAME,0,nptr);
    if (nptr->ty>0) { // should be function or code
	// type=list3(FUNCTION,type,arg);
	type=list3(car(nptr->ty),cadr(nptr->ty),caddr(nptr->ty));
    } else { // label
	type=nptr->ty;
    }
    getsym(0);
    // extrn_use(nptr);  can be static
    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=list3n(GVAR,0,nptr);
	    type=nptr->ty;
	    getsym(0);
	    extrn_use(nptr);
	    break;
	case FLABEL: case BLABEL:
	    return fname(nptr);
	case FUNCTION: case CODE:
	    error(-1);          // FNAME have to be used
	    break;
	case LVAR:
	case IVAR:
	case LREGISTER:
	case DREGISTER:
	case FREGISTER:
	case REGISTER:
	    e1=list3n(nptr->sc,nptr->dsp,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(stmode==GOTO?CODE:FUNCTION,INT,0);
                nptr->sc = EXTRN1;
		nptr->ty= type;
		extrn_use(nptr);
		e1=expr15(list3n(FNAME,0,nptr));
		break;
	    } else if (in_macro_if) {
		type = INT;
		e1= list2(CONST,0);
		break;
	    } else {
		if (stmode!=GOTO) // undefined on goto statement is OK
		    error(UDERR);
		smode = mode; mode = GDECL;
                // type = nptr->ty= glist3(FUNCTION,INT,0);
                type = INT;
		def(nptr,0);
		nptr->sc = EXTRN1;
		mode = smode;
		e1=list3n(FNAME,0,nptr);
		// type=list3(nptr->sc,nptr->ty,nptr->dsp);
		break;
	    }
	default:error(UDERR);
	}
	break;
    case STRING:
	conv-> string_(nptr->nm,nptr->dsp);
	e1=list3n(STRING,nptr->dsp,nptr);
	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);
	}
	// this is wrong... should be
	//  code (*)(return_type);
	type=list2(POINTER,list3(CODE,VOID,cadr(fnptr->ty)));
	//                          type   arg
	e1=list3n(RETURN,0,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:
	// return current frame pointer (or equivalent)
	conv-> environment_();
	type=list2(POINTER,VOID);
	e1=list2(ENVIRONMENT,0);
	getsym(0);
	break;
    case C_FILE:
	// return current file name
	nptr=get_name(filep->name0,0,0);
	type=list3(ARRAY,CHAR,nptr->dsp);
	e1=list3n(STRING,nptr->dsp,nptr);
	getsym(0);
	break;
    case C_FUNCTION:
	// return current function name
	nptr=get_name(fnptr->nm,0,0);
	type=list3(ARRAY,CHAR,nptr->dsp);
	e1=list3n(STRING,nptr->dsp,nptr);
	getsym(0);
	break;
    case C_LINE:
	// return current lineno in int
	type=UNSIGNED;
	e1=list2(CONST,lineno);
	getsym(0);
	break;

    case LPAR:
	conv->lpar_();
	getsym(0);
	qualifiers();

	/* type cast */

	if(typeid(sym)) {
	    t=typename();
	    conv->type_(t,0,0);
	    conv->rpar_();
	    checksym(RPAR);
	    if (sym==LC && (t>0 && (car(t)==STRUCT||car(t)==UNION))) {
		//    initializer
		//             q->lock = (spinlock_t) { };
		conv->lc_();
		if (mode==GDECL||inmode) {
		    int e2,e3;
		    smode = mode;
		    type = t;
		    nptr0=new_static_name("__lstruct",'_');
		    mode=STADECL;
		    def(nptr0,0);
		    e1 = size(type);
		    mode = smode;
		    e2 = list3n(GVAR,0,nptr0);
		    e3 = decl_data_field(type,e2,0);
		    if (!inmode) {
			e1 = list3(RSTRUCT,e2,e1);
		    } else {
			e1 = reverse0(e3);
			e1 = list3(DECL_DATA,e1,t);
			// e1 = list3(RSTRUCT,e2,e1);
			e1 = list4(CAST,e1,t,t); // only for cast syntax
		    }
		} else {
		    int e2;
		    nptr0 = get_nptr(); // should be freed in a scope?
					// in case of inline, we cannot
		    nptr0->nm = "";
		    nptr0->sc = EMPTY;
		    nptr0->attr = 0;
		    type = nptr0->ty = t;
		    e1 = size(type);
		    def(nptr0,0);
		    e2 = list3n(nptr0->sc,nptr0->dsp,nptr0);
#if LOCAL_STRUCT_INIT_STATIC 
		    local_struct_static(e2); 
#else
		    decl_data_field(type,e2,0);
#endif
		    e1 = list3(RSTRUCT,e2,e1);
		}
		if (init_vars && mode!=LDECL) {
		    emit_init_vars();
		}
		conv->rc_();
		checksym(RC);
		type = t;
		return e1;
	    }
	    e1=expr13();
	    if (inmode) { 
		e1 = list4(CAST,e1,t,type);
		type = t;
		return e1;
	    } else
		return correct_type(e1,t);
	} else if (sym==LC) {
	    // statement in expression  (GNU extension)
	    //    a = {hoge(); 1;}
	    // In docomp, last expression is kept in lastexp.
	    //
	    if (inmode) {
		int sparse = parse; parse=0;
		docomp(1);
		// wrong parse tree fix me!
		e1 = list3(COMMA,reverse0(parse),lastexp);
		parse = sparse;
		lastexp = 0;
	    } else {
		int l,b,l2,cntl=control;
		// if COMMA expr is not gexpred by !control, 
		// l2 is not defined and generates undefined error.
		// cntl prevents this. 
		if (cntl) {
		    gen_jmp(l=fwdlabel());
		} else l = 0;
		b = backdef(); // control=1
		docomp(1);
		if (cntl) {
		    gen_jmp(l2=fwdlabel());
		} else l2 = 0;
		// make it a simple call (by jmp)
		// register stack save now
		e1 = list3(COMMA,list3(LCALL,b,l2),lastexp);
		lastexp = 0;
		if (l) fwddef(l);
		control=cntl;
	    }
	} else {
	    e1=expr0();
	}
	conv->rpar_();
	checksym(RPAR);
	break;
    default:error(EXERR); e1=list2(CONST,0);
    }
    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);
	    if (inmode || chk) {
		e1 = list5(ARRAY,e1,e2,t,type);
		if (car(t)!=POINTER)
		    error(-1);
		type = cadr(t);
	    } else {
		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) {
	set_attr(ncaddr(e1),FNAME,1);
	type=list2(POINTER,type);
    }
    return e1;
}

/* function call */

static NMTBL *
make_tmp_struct()
{
    int sz;
    /* checks type */
    /* make temporary struct for return value */
    /* but it is better to see we can reuse old one */
    /*      a = f().a case  */
    if (tmp_struct && !inmode) {
	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,0);
	}
    } else if (tmp_struct && inmode) {
	if (size(type)>size(tmp_struct->ty)) {
	    tmp_struct->ty = type;
	}
    } else {
	tmp_struct = def(0,0);
	if (inmode) {
	    set_attr(tmp_struct,HAS_ADDRESS,1);
	    parse = list5n(ST_DECL,parse,list3(LDECL,0,0),0,tmp_struct);
	}
    }
    return tmp_struct;
}

/*
    function call

 */
static int
expr15(int e1)
{
    int t,arglist,e,argtypes,at,ftype;
    int type0 = type_value(type);
    int dots;

    /* function call target */

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

    /* function argments */

    function_type(ftype,&dots);

    if (type0<=0) {
	getsym(0);
	return INT;
    }
    argtypes = caddr(type0);
    if (!argtypes) dots=1;
    if ((t=type_value(cadr(type0)))>=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;
	    if (!dots) {
		error(AGERR);
	    }
	} else if(car(argtypes)==DOTS) at=DOTS;
        else { at=car(argtypes); argtypes=cadr(argtypes); }
	if (at>0&&car(at)==ARRAY) at = list2(POINTER,cadr(at));
	e = correct_type(e,at);
	arglist=list3(e,arglist,type);
	if(sym!=COMMA) break;
	conv->comma_();
	getsym(0);
    }
    if (!dots && argtypes && car(argtypes)!=VOID) {
	error(AGERR);
    }
    checksym(RPAR);
    conv->funcall_args_(e1,ftype,arglist);
    if(t==CODE) {
	if (stmode!=GOTO) error(FNERR);
	// code segment has no return type
	type = ftype;

	return list4(CODE,e1,arglist,ftype); // should be CODE?
    } else if (stmode==GOTO) {
	// symbol is undefined at this time
	// error(GTERR);
    }


    /* return type */

    type = cadr(ftype); 
    type0 = type_value(type);
    if(type0==CHAR||type0==SHORT) type=set_type_with_attr(INT,type);
    else if(type0==UCHAR||type0==USHORT) type=set_type_with_attr(UNSIGNED,type);
    else if(type0>0 && (car(type0)==STRUCT||car(type0)==UNION)) {

	/* temporal struct is required */

	NMTBL *tmp = make_tmp_struct();

	e = list3n(inmode?IVAR:LVAR,tmp->dsp,tmp);

	/* 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));
    }
    if (car(e1)==FNAME) {
	// recursive inline is not allowed
	if (ncaddr(e1)!=fnptr && is_inline(ncaddr(e1))) {
	    return list4(INLINE,e1,arglist,ftype);
	}
    }
    return list4(stmode==GOTO?CODE: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: case VOLATILE:
	    return 1;
	case IDENT: return nptr->sc==TYPE;
    }
    return (integral(s));
}

static int
typename(void)
{
    int t;
    int sctmode=ctmode;
    int sd = sdecl_f;
    sdecl_f = 0;
    ctmode=0;
    sdecl_f = 0;

    type=t=typespec();  // undefine case?
    ctmode = sctmode;
    ndecl0();
    reverse(t);
    sdecl_f = sd;
    return type;
}

/*
    type prefix in cast
	(type definition)expre
 */

static int
ndecl0(void)
{
    if(sym==MUL) {
	attribute = 0; // do not inherite type attributes
	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 = inmode?cexpr(pexpr(expr(1))):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 cheap management

compiler keeps string data (name, string etc.)

    new_cheap()     increment cheap memory

    increment_cheap(cheap, &pointer_top)
	before this
	    pointer_top = cheap->ptr
	is necessary.

	increment cheap->ptr, if cheap is increased,
	pointer_top is updated.

	Note.   Inline comments use cheap during getc(). If getch()
		cross the new line, continuous cheap is not assured.

    save_cheap(struct cheap *scheap,struct cheap *cheap)
	saved point for reseting cheap.
    reset_cheap(struct cheap *scheap)
	abandone cheap to save_cheap point.

    To avoid interference, allocate new cheap pool and struct.
 */

extern 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);
    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-1) {
	// running out of cheap area
	if (!cheap->next) { 
	    // no previously freed cheap create new one
	    cheap->next = new_cheap();
	}
	if (save) {
	    // string in cheap have to be continuous.
	    // prepare move from old cheap
	    cheap->ptr = from = *save;
	    i = cheap->last - from;
	    to   = *save = cheap->next->first;
	    if (i>CHEAPSIZE) error(NMERR); // a string is larger than cheap size
	    if (to<=chptr && chptr<=to+i) {
		// we are going to destory the line just we are
		// reading... move reading line to the next cheap
		if (!cheap->next->next) { 
		    cheap->next->next = new_cheap();
		}
		q = chptr;
		p = chptr = cheap->next->next->ptr;
		while((*p++ = *q++));
		// now save to move overflowed string
	    }
	    while(i-->0) {
		*to++ = *from++;
	    }
	    cheap->next->ptr = to-1;
	}
	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
    //   (should be while loop
    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
	hash table
	lexical scope structure
	    for macro and local variable, goto label, string
 */

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;
    int i,j;

    eptr = iptr= &htable[hash % GSYMS];
    for(i=0;(hptr=*iptr) && hptr->sc!=0 && neqnamel(hptr->nm,name,len);i++) {
	// open hash
	if (++iptr== &htable[GSYMS])
	    iptr=&htable[0];
	if (eptr==iptr) error(GSERR);
    }
    if (HEAP_REPORT && i>3) {
	for(j=0,eptr=htable;eptr<htable+GSYMS;eptr++) {
	    if (*eptr) j++;
	}
	fprintf(stderr,"# bad hash %d hash=%d/%d %02d%% %s\n",
		i, hash%GSYMS,GSYMS,j*100/GSYMS,name);
    }
    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 c,i = 0;
    unsigned int hash0 = 0;
    char *n = name;
    struct cheap scheap;

    save_cheap(&scheap,cheap);
    c = *n++;
    for(i=0;alpha(c) || digit(c);i++) {
	hash_value(hash0,c);
	c = *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);
}

/*
     All strings are kept in hash table, and shared.
          float/long long should be shared too.
 */
static void
getstring(void)
{
    char *name = cheap->ptr;
    int i= 0;
    int c;
    unsigned int hash = 0;
    struct cheap scheap;

    save_cheap(&scheap,cheap);
    do {
	while (ch == '"') {
	    in_quote = 1;
	    getch();  
	    while (ch != '"') {
		if (i>STRSIZE) error(STRERR);
		if ((c = escape())==0 && ch=='"') {
		    in_quote = 0;
		    getch();
		    goto possible_string_concatenate;
		}
		hash_value(hash, *cheap->ptr = c);
		cheap = increment_cheap(cheap,&name);
		i++;
	    }
	    in_quote = 0;
	    getch();
	}
possible_string_concatenate:
	skipspc();
    } while (ch=='"'); 
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,&name);
    i++;
    nptr = name_space_search(hash_search(name,&scheap,i,hash,DEF),STRING);
    // if we already have this, hash_search will reset cheap
    //    should do the same thing for float/double constant
    nptr->dsp = i;
    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'||ch=='f') 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'||ch=='f') {
		*cheap->ptr = ch;
		cheap = increment_cheap(cheap,&num);
		getch();
		if ((ch=='-' && (cheap->ptr[-1]=='e'|| cheap->ptr[-1]=='f'))||
		    (ch=='+' && (cheap->ptr[-1]=='e'|| cheap->ptr[-1]=='f'))) {
		    *cheap->ptr = ch;
		    cheap = increment_cheap(cheap,&num);
		    getch();
		}
	    }
	    /* C99 */
	    if (ch=='F' || ch=='L') {
		//   float or long double
		*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;

retry:
    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(scaddr(nptrm->dsp),nptrm->nm)) ||
	    (nptrm->sc==FMACRO&&skipspc()=='(')) {
	    char *s;
	    if (in_macro_if && (s = scaddr(nptrm->dsp)) && *s==0) {
		symval = 0;
		return (sym=CONST);
	    }
	    if (!check_recurse(nptrm->nm,macro_history)) {
		macro_expansion(nptrm);
		sc=0; goto retry;
	    }
	}
        /* global variable name table */
	nptr0 = name_space_search(nlist,sc);
	if (nptr0->sc == RESERVE) 
	    return sym = nptr0->dsp;
	if (mode==ATTRIBUTE && nptr0->sc == ATTRIBUTE) 
	    return sym = nptr0->dsp;
	sym = IDENT;
	gnptr=nptr=nptr0;

	switch(mode) {
	case ADECL: if (nptr0->sc ==TYPE) return sym; break;
	case GDECL: case GSDECL: case GUDECL: case LSDECL: case LUDECL: 
	case GTDECL: case TOP: case GEDECL: case SFDINIT:
	    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 LDECL ony? */
	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();
	    sc = 0; goto retry;
	}
	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();
	sc = 0; goto retry;
    case 0:
    case '\n':
    case '\f':
    case '\\':
	sc = 0; goto retry;
    default:
	error(CHERR);
	sc = 0; goto retry;
    }
}


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->u.nptr = free_nptr_list;
    free_nptr_list = n;
}

/*
     nptr pool (for resue)
 */

extern NMTBL *
get_nptr()
{
    NMTBL *ret;
    if (free_nptr_list) {
	// we can use free list
	ret = free_nptr_list;
	free_nptr_list = free_nptr_list->u.nptr;
	ret->sc = 0;
	ret->ty = 0;
	ret->dsp = 0;
	ret->dsp = 0;
	ret->u.nptr = 0;
	ret->attr = 0;
	ret->next = 0;
	return ret;
    }
    if (nptr_pool->ptr >= nptr_pool->last) {
	if (nptr_pool->next) {
	    // we can reuse previous nptr_pool
	    nptr_pool = nptr_pool->next;
	} else {
	    // allocate new one
	    if (HEAP_REPORT)
		fprintf(stderr,"** nptr extended\n");
	    nptr_pool->next = new_cheap();
	    nptr_pool = nptr_pool->next;
	}
    }
    ret = (NMTBL *)nptr_pool->ptr;
    nptr_pool->ptr += sizeof(NMTBL);
    ret->sc = 0;
    ret->ty = 0;
    ret->dsp = 0;
    ret->u.nptr = 0;
    ret->attr = 0;
    ret->next = 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;
    nptr->u.nptr=0;
    return nptr;
}

/*
    lexical name scope handler
	enter_scope
	leave_scope


    nptr->dsp
	glist3n(ScopeID,next,nptr)
	    ScopeID  MACRO,NAME,TYPEDEF,FIELD
 */

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

    for(ns=hash->dsp;ns;ns=cadr(ns)) { // iterate on possible scope
	if (car(ns)==sc) {
	    return ncaddr(ns);
	}
    } 
    if (ns==0) {
	n = get_nptr();
	hash->dsp = glist3n(sc,hash->dsp,n); // make a scope for sc
    }
    n->nm = hash->nm;
    n->sc = EMPTY;
    n->dsp = 0;
    n->attr = 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)
extern 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)) { // iterate on possible scope
	if (car(ns)==sc) {
	    // memorize previous nptr for this name for leave_scope
	    car(current_scope) = glist3n(ns,car(current_scope),nptr1);
	    ncaddr(ns) = nptr1 = get_nptr();
	    nptr1->nm = nlist->nm; nptr1->sc=EMPTY; nptr1->dsp = 0;
	    break;
	}
    }
    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) {
	    car(scope) = glist3n(ns,car(scope),nptr1);
	    ncaddr(ns) = nptr1 = get_nptr();
	    nptr1->nm = nlist->nm; nptr1->sc=EMPTY; nptr1->dsp = 0;
	    break;
	}
    } 
    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 = ncaddr(car(scope));
	switch(ns->sc) {
	case GVAR: case STATIC: case IVAR: break;
	default: if (!inmode) free_nptr(ns);
	}
	ncaddr(car(scope)) = ncaddr(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 NMTBL *
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);
    return nptr0;
}

/*
    Character handling
 */

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;
}

static int prev_macro_end;

extern int
getch(void)
{
    int i,j;
    if (prev_macro_end) {
	int history = 0;
	if (macro_history_save) {
	    history = car(macro_history_save);	
	    int save = cadr(macro_history_save);	
	    free_glist2(macro_history_save);
	    macro_history_save = save;
	    while(macro_history!=history) {
		int next = cadr(macro_history);
		free_glist3(macro_history);
		macro_history = next;
	    }
	} 
	prev_macro_end = 0;
    }
    if(*chptr) 
	return ch = *chptr++;
    else if (chptrsave) {
	prev_macro_end = 1;

	chptr = scaddr(chptrsave);
	ch = car(chsave);
	i = cadr(chptrsave);
	j = cadr(chsave);
        free_glist2s(chptrsave);
        free_glist2(chsave);
	chptrsave = i;
	chsave = j;
	return ch;
    }
    if (!filep) return ch=0; // command line case;
    getline();
    if (in_macro_if) check_macro_eof();
    return getch();
}

static int
escape(void)
{
    char c;
    // Unicode?

    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((sizeof(int)+size_of_longlong)/sizeof(int));
    heap[e]=e1;
    lcadr(e)=d1;
    return e;
}

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

    e=getfree((sizeof(int)+size_of_longlong)/sizeof(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((sizeof(int)+sizeof(double))/sizeof(int));
    heap[e]=e1;
    dcadr(e)=d1;
    return e;
}

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

    e=getfree((sizeof(int)*2+sizeof(double))/sizeof(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
list3n(int e1, int e2, NMTBL *e3)
{
    int e;

    e=getfree(2+(sizeof(void*)/sizeof(int)));
    heap[e]=e1;
    heap[e+1]=e2;
    vcaddr(e) = e3;

    return e;
}

extern int
list3s(int e1, int e2, char *e3)
{
    int e;

    e=getfree(2+(sizeof(void*)/sizeof(int)));
    heap[e]=e1;
    heap[e+1]=e2;
    scaddr(e) = 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;
}

extern int
list4n(int e1, int e2, int e3, NMTBL * e4)
{
    int e;

    e=getfree(3+(sizeof(void*)/sizeof(int)));
    heap[e]=e1;
    heap[e+1]=e2;
    heap[e+2]=e3;
    ncadddr(e) = e4;
    return e;
}

extern int
list4s(int e1, int e2, int e3, char * e4)
{
    int e;

    e=getfree(3+(sizeof(void*)/sizeof(int)));
    heap[e]=e1;
    heap[e+1]=e2;
    heap[e+2]=e3;
    scadddr(e) = e4;
    return e;
}

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

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

extern int
list5n(int e1, int e2, int e3, int e4,NMTBL * e5)
{
    int e;

    e=getfree(4+(sizeof(void*)/sizeof(int)));
    heap[e]=e1;
    heap[e+1]=e2;
    heap[e+2]=e3;
    heap[e+3]=e4;
    ncaddddr(e) = e5;
    return e;
}

extern 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:
	case ATTRIBUTE:
	    e=gfree;
	    gfree+=n;
	break;
    default:
	if (inmode) {
	    e=gfree;
	    gfree+=n;
	} else {
	    lfree-=n;
	    e=lfree;
	}
    }
    if(lfree<gfree) error(HPERR);
    return e;
}

extern int
glist2(int e1,int e2)
{
    int ret;
    if (free_glist2_list) {
	ret = free_glist2_list;
	free_glist2_list = cadr(free_glist2_list);
	car(ret)=e1; cadr(ret)=e2;
	return ret;
    }
    ret = gfree;
    gfree+=2 ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    if(lfree<gfree) error(HPERR);
    return ret;
}

extern int
glist2s(int e1,char *e2)
{
    int ret;
    ret = gfree;
    gfree+=(1+sizeof(void*)/sizeof(int)) ;
    heap[ret]=e1;
    scaddr(ret) = e2;
    if(lfree<gfree) error(HPERR);
    return ret;
}

extern int
glist3s(int e1,int e2, char *e3)
{
    int ret;
    ret = gfree;
    gfree+=(2+sizeof(void*)/sizeof(int)) ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    scaddr(ret) = e3;
    if(lfree<gfree) error(HPERR);
    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 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;
    }
    ret = gfree;
    gfree+=3 ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    heap[ret+2]=e3;
    if(lfree<gfree) error(HPERR);
    return ret;
}

extern int
glist3n(int e1,int e2,NMTBL *e3)
{
    int ret;
    ret = gfree;
    gfree+=(2+sizeof(void*)/sizeof(int)) ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    ncaddr(ret) = e3;
    // heap[ret+2]=e3;
    if(lfree<gfree) error(HPERR);
    return ret;
}


extern int
glist4(int e1,int e2,int e3,int e4)
{
    int ret;
    ret = gfree;
    gfree+=4 ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    heap[ret+2]=e3;
    heap[ret+3]=e4;
    if(lfree<gfree) error(HPERR);
    return ret;
}

extern int
glist4n(int e1,int e2,int e3,NMTBL * e4)
{
    int ret;
    ret = gfree;
    gfree+=(3+sizeof(void*)/sizeof(int)) ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    heap[ret+2]=e3;
    ncadddr(ret) = e4;
    if(lfree<gfree) error(HPERR);
    return ret;
}

extern int
glist5(int e1,int e2,int e3,int e4,int e5)
{
    int ret;
    ret = gfree;
    gfree+=4 ;
    heap[ret]=e1;
    heap[ret+1]=e2;
    heap[ret+2]=e3;
    heap[ret+3]=e4;
    heap[ret+4]=e5;
    if(lfree<gfree) error(HPERR);
    return ret;
}

extern void
free_glist2s(int e1)
{
    // fix me
}

extern void
free_glist3(int e1)
{
    /* list3n is larger than length 3, but it's Ok */
    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)  // free all chain of glist3
{
    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
append5(int p,int a1,int a2,int a3,int a4)
{
    int p1;
    if(!p) return list5(a1,0,a2,a3,a4);
    p1=p;
    while(cadr(p)) p = cadr(p);
    cadr(p) = list5(a1,0,a2,a3,a4);
    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;
}

/*
    attribute list for nptr->attr
 */

extern int
has_attr(NMTBL *n,int attr)
{
    int e;
    if (!n) return 0;
    e = n->attr;
    for(;e;e=cadr(e)) {
	if (car(e)==attr) return 1;
    }
    return 0;
}

extern int
attr_value(NMTBL *n,int attr)
{
    int e;
    if (!n) return 0;
    e = n->attr;
    for(;e;e=cadr(e)) {
	if (car(e)==attr) return caddr(e);
    }
    return 0;
}

extern void
set_attr(NMTBL *n,int attr,int value)
{
    int e;
    if (!n) error(-1);
    e = n->attr;
    for(;e;e=cadr(e)) {
	if (car(e)==attr) {
	    caddr(e) = value;
	    return;
	}
    }
    switch (n->sc) {
    case LVAR:
	n->attr = list3(attr,n->attr,value);
	break;
    default:
	n->attr = glist3(attr,n->attr,value);
	break;
    }
    return;
}

/*
    transfer attribtes() value to nptr;
 */

extern void
set_attributes(NMTBL *n,int attr) {
    int e;
    for(e = attr; e ; e = cadr(e)) {
	set_attr(n,car(e),caddr(e));
    }
}

extern int
attr_value_in_list(int list,int attr)
{
    int e;
    e = list;
    for(;e;e=cadr(e)) {
	if (car(e)==attr) return caddr(e);
    }
    return 0;
}

static void
copy_attributes(NMTBL *n) {
    int attr;
    for(attr=n->attr;attr;attr=cadr(attr)) {
	attribute = list3(car(attr),attribute,caddr(attr));
    }
}


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);
}

/*
    type attribute
	type = list3(ATTRIBUTE,type,attr);
    use type_value to remove this
 */

extern int
set_type_attr(int type,int attr)
{
    if (type>0 && car(type)==ATTRIBUTE) {
	caddr(type) = attr;
    } else {
	type = list3(ATTRIBUTE,type,attr);
    }
    return type;
}

extern int
gset_type_attr(int type,int attr)
{
    if (type>0 && car(type)==ATTRIBUTE) {
	caddr(type) = attr;
    } else {
	type = glist3(ATTRIBUTE,type,attr);
    }
    return type;
}

extern int
get_type_attr(int type)
{
    if (type>0 && car(type)==ATTRIBUTE) {
	return caddr(type);
    } else {
	return 0;
    }
}

extern int
type_value(int type)
{
    if (type>0 && car(type)==ATTRIBUTE) {
	return cadr(type);
    } else {
	return type;
    }
}

extern int
set_type_with_attr(int type,int type_with_attr)
{
    if (type_with_attr>0 && car(type_with_attr)==ATTRIBUTE) {
	return list3(ATTRIBUTE,type_value(type),caddr(type_with_attr));
    } else {
	return type;
    }
}

extern int
gset_type_with_attr(int type,int type_with_attr)
{
    if (type_with_attr>0 && car(type_with_attr)==ATTRIBUTE) {
	return glist3(ATTRIBUTE,type_value(type),caddr(type_with_attr));
    } else {
	return type;
    }
}

/* 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 int c4(int d)  { fprintf(stderr,"heap[%d]=",d);return caddddr(d); }

extern char *cc2(int d) { fprintf(stderr,"heap[%d]=",d);return scaddr(d); }
extern NMTBL *nc2(int d) { fprintf(stderr,"heap[%d]=",d);return ncaddr(d); }

/* end */