view mc-parse.c @ 897:66f32d0af4d2

local struct initialization target management in decl
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 07 Apr 2014 10:27:12 +0900
parents d712ee10feb7
children c073495301ae
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
// this in ./stdio.h
//extern     void * malloc(size_t size);
//extern     void * realloc(void *ptr, size_t size);



#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 struct initialzation in static area
int local_struct_offset;         // local struct initialzation offset

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
int lp64;                         // LP64 mode (long = (int *) = 8)

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 int 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_def(NMTBL *n, int t, int v,int offset,int skip);
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) ;
int types_compatible(int t, int t1);

// 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((void*)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
print_string(char *s, int i)
{
    int c;
    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("\"");
}

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 STRINGS:
            {
                int i;
                for(i = nptr->dsp;i; i = cadr(i)) {
                    print_string(scaddr(i), car(i));
                    printf("\n");
                }
                break;
            }
        case STRING:
            print_string(nptr->nm, nptr->dsp);
            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("__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("__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("__builtin_types_compatible_p",BUILTIN_TYPES_COMPATIBLE_P,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);
    reserve("__always_inline__",ALWAYS_INLINE,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;
    }
    getline1();
}

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,attr;
    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;
    attr = attribute; // decl0 will clear it
    n=decl0();
    reverse(t);
    if (n == &null_nptr) {
        /* only bit field allows null identifier */
        if (!(type>0&&car(type)==BIT_FIELD)) {
            error(DCERR); return;
        }
    } else 
        set_attributes(n,attr);
    while (sym==ATTRIBUTE||sym==ASM) { 
        int sym0 = sym; getsym(0); attributes(sym0); 
    }
    set_attributes(n,attribute);
    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_def(n, 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_def(n, 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") 
        if (sym==STRING) {
            attribute = list3n(ASM,attribute,nptr);
            getsym(0);
        }
        mode = smode;
        checksym(RPAR);
        return;
    }
    while(sym!=RPAR) {
        if (sym==LPAR) {
            attributes(0);   //     __attibte(())
        } 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 CHAR:
        if ((char)-1 == 255) { t = UCHAR; getsym(0); break; }
    case VOID:
    case INT:
    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;  // default
        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);   // signed short int
        } else if (sym==LONG) {             // signed long
            getsym(0); 
            t = lp64?LONGLONG:INT;
            if(sym==LONG) {                 // signed long long
                if(getsym(0)==INT) getsym(0);
                t=LONGLONG;
            } else if(sym==INT) {           // signed long int
                getsym(0);
                t = lp64?LONGLONG:INT;
            }
        }
        break;
    case UNSIGNED:
        t = UNSIGNED;
        if(getsym(0)==INT) {
            t = UNSIGNED;         // unsigend int
            getsym(0);
        } else if (sym==CHAR) { getsym(0); t = UCHAR; }
        else if (sym==SHORT) {    // unsigend short
            t = USHORT; 
            if(getsym(0)==INT) getsym(0);
        } else if (sym==LONG) {      // unsigned long
            getsym(0); 
            t = lp64?ULONGLONG:UNSIGNED;
            if(sym==LONG) {          // unsigned long long
                if(getsym(0)==INT) getsym(0);  // unsigend long long int
                t=ULONGLONG;
            } else if(sym==INT) {    // unsigend long int
                getsym(0);
                t = lp64?ULONGLONG:UNSIGNED;
            }
        }
        break;
    case SHORT:
        t=SHORT;
        if(getsym(0)==INT) getsym(0);
        else if(sym==UNSIGNED) {
            getsym(0); t = USHORT;
        }
        break;
    case LONG:
        t = lp64?LONGLONG: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);  // long unsigend int
          t = lp64?ULONGLONG:UNSIGNED;
        } //
        break;
    case TYPEOF:
        getsym(0);
        slfree=lfree; stype=type;
        smode = mode; mode = STAT;
        checksym(LPAR);
        mode = LDECL;  // typespec required this
        if((t=typename())==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;
}

NMTBL *
reversen(NMTBL *t1)
{
    NMTBL *t2,*t3;

    t2= &null_nptr;
    while(t1 != &null_nptr) {
        t3=t1->next;
        t1->next = 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;
        case POINTER: return size_of_pointer;
        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 0

static int
decl_data_field(int type1,int v,int offset)
{
    int t1,t2,period=0;
    // int offset0 = offset+size(type);
    int mode_save=mode;
    int type0=type_value(type1);
    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 != PERIOD ) {
         // have to be a value, no comma cascading values
         // int mode_save = mode;
         int stype = type;
         //  mode=STAT;  do it in GDECL mode
         //  expr1 may contain (sturct hoge){...}. it will call local_struct_static. tell him about offset
         local_struct_offset = offset;  
         if (mode!=SFDINIT) mode = STAT;
         int e=expr1();
         mode = mode_save;
         int t2 = type;
         type = stype;
         if (size(t2)==size(type0)) {
             // direct assingn case , including (struct hoge){ ... } 
             if (inmode || mode==SFDINIT) {
                 return e;
             }
             type = type0;
             offset = assign_data(e,type0,v,offset);
             type = stype;
             return offset;
         }
         // missing brace case
         error(SIERR);
         if (inmode) {
             offset = list4(DECL_DATA_LIST,offset,list3(DECL_DATA,e,t2),car(t1));
         } else {
             type = car(t1);
             offset = assign_data(e,car(t1),v,offset);
             type = stype;
         }
         t1 = cadr(t1);
         if (! ( t1 && sym==COMMA)) return offset;
         conv->comma_(); getsym(0);  // fall through
    }
    mode=SFDINIT;
    while(1) {
        if (sym==PERIOD) { /* struct/union field initializaer */
            period=1;
            getsym(0);
            if (sym==IDENT) {
                int foffset;
                nptr1 = nptr;
                t2 = search_struct_type(type1,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);  
                offset = list4(DECL_DATA_LIST,offset,offset1,car(t1));
            } else {
                offset = decl_data(car(t1),v,offset,1);  
            }
            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 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 (local_nptr || mode==SFDINIT || inmode )  {
        decl_data_field(type,v,local_struct_offset);
        return;
    }
    local_nptr = n;  // will be clear in flush
    local_struct_offset = offset;

    mode=STADECL;
    nptr0=new_static_name("__lstruct",'_');
    def(nptr0,0);
    mode=smode;
    v0 = list3n(GVAR,0,nptr0);
    decl_data_field(type,v0,offset);
    // do struct assignment before flushed assignment expression
    // int sz = size(n->ty);
    //init_vars = list2(
    //    list4(STASS,
    //        inmode?(
    //            offset?(
    //                lp64?list3(LADD,list3n(IVAR,n->dsp,0),llist2(LCONST,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;
    gen_delayed_decl_data(v,0);
    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_def(NMTBL *n, int t, int v,int offset,int skip)
{
    NMTBL *slocal_nptr = local_nptr;
    local_nptr = n;
    int slocal_struct_offset = local_struct_offset ;
    int e = decl_data(t,v,offset,skip);
    local_struct_offset = slocal_struct_offset ;
    local_nptr = slocal_nptr;
    return e;
}

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 ((t=type_value(cadr(t0)))==CHAR||t==UCHAR) {
            e=expr1();
            mode = mode_save;
            if(car(e)!=STRING && car(e)!=STRINGS)
                error(TYERR);
            if (inmode) {
                offset = list3(DECL_DATA,e,type);
                return offset;
            }
            offset=assign_data(e,list3(ARRAY,t,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) {   
            local_struct_offset = offset;
            local_struct_static(v); 
            // change mode to STADECL
            // decl_data_field(t,n,offset) is called inside;
        } else
#endif
        // we cannot handle direct struct assign ( struct hoge a = f(); ) here, because of missing brace case
        if (inmode) {
            int offset1=decl_data_field(t,v,0);
            if (car(offset1)==DECL_DATA_FIELD||car(offset1)==DECL_DATA_LIST)
                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_();
}

extern void
code_arguments_fix(NMTBL *fnptr)
{
    arg_register(fnptr, inmode);
}

/* code sgement
     simpler than fdecl, because it does not have return value.
 */
static void
code_decl(NMTBL *n)
{
    int 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) 
        code_arguments_fix(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 (has_attr(n,ALWAYS_INLINE)) {
        inmode = INLINE;
    }
    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;
    arg_register(fnptr,inmode);
    //  arg_register changes first arguments offset, we have to follow.
    //  In inmode case, we don't have to do this, because it was IVAR.
    if (!inmode && struct_return) { cadr(car(struct_return)) = ncadddr(fnptr->dsp)->dsp;}
    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
        inmode = sinmode;
        if (sinmode && n->sc==STATIC) return;
        if (has_attr(n,ALWAYS_INLINE)) return;
        inmode = 0;
        pfdecl(fnptr);
        inmode = 0;
    }
    inmode = sinmode;
}

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

static int
copy_arg(int arg, int is_code) 
{
    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, is_code);

        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,ALWAYS_INLINE)) return;
    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, 0);

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

    disp=0;
    arg_register(fnptr,0); // 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;
}

/*
    static inline function  is not generated, but global inline fuction may
    called directlry. so we have to generated this. this is also called from
    parse mode.
 */
extern void
pcode_decl(NMTBL *n)
{
    int e;
    int arg,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_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,1);

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

    code_arguments_fix(fnptr);
    
    retcont = 0;
    tmp_struct = 0;

    disp = -args;
    arg_register(fnptr, 0); // 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 && sym!=STRINGS) error(DCERR);
    asm0=list3n(sym,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 && sym!=STRINGS) error(DCERR);
        out=list2(list3n(sym,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 && sym!=STRINGS) error(DCERR);
            input=list2(list3n(sym,nptr->dsp,nptr),input);
            getsym(0);
            e1=list2(expr1(),e1);
        } while(sym==COMMA);
    }
    if (sym==COLON) {
        do {
            // option string
            getsym(0);
            if (sym!=STRING && sym != STRINGS) error(DCERR);
            opt=list2(list3n(sym,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());
#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)) return list2(LNOT,e);
        if (type==LONGLONG||type==ULONGLONG) return binop(EQ,e,llist2(LCONST,0),type,LONGLONG);
        if (type==FLOAT||type==DOUBLE) return binop(EQ,e,dlist2(FCONST,0),type,FLOAT);
        error(TYERR);
        return list2(CONST,1);
    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_TYPES_COMPATIBLE_P:
        {
         int t, t1;
        getsym(0);
        int slfree=lfree; int stype=type;
        int smode = mode; mode = STAT;
        checksym(LPAR);
        mode = LDECL;  // typespec required this
        if((t=typename())==0) {
            mode = STAT;   // too late for expression 
            expr(0); 
            t = type;
        }
        checksym(COMMA);
        mode = LDECL;  // typespec required this
        if((t1=typename())==0) {
            mode = STAT;   // too late for expression 
            expr(0); 
            t1 = type;
        }
        set_lfree(slfree); type=stype;
        mode = smode;
        checksym(RPAR);
        return types_compatible(t,t1);
        }
    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) ? 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) ? 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
expr_identifier()
{
    int e1;
    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 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);
            int 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); e1 = 0;
    }
    return expr16(e1);
}

static int 
typecast() 
{
    int e1;
    NMTBL *nptr0;
    int 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_();
        nptr0 = get_nptr(); 
        nptr0->nm = "";
        nptr0->sc = EMPTY;
        nptr0->attr = 0;
        type = nptr0->ty = t;
        int local_offset;
        if (mode==GDECL||inmode) {
            int e2,e3;
            int sz = size(type);
            e2 = list3n(GVAR,0,nptr0);
            e3 = decl_data_field(type,e2,0);
            if (!inmode) {
                e1 = list3(RSTRUCT,e2,sz);
            } else {
                e1 = reverse0(e3);
                e1 = list3(DECL_DATA,e1,t);
                e1 = list4(CAST,e1,t,t); // only for cast syntax
            }
        } else {
            if (!local_nptr) {
                // struct initialization for this variable
                //   (struct hoge) {  .fuga = 5 }
                local_nptr = nptr0;
                int smode = mode; mode = LDECL;
                def(nptr0,0);
                mode = smode;
                local_struct_offset = 0;
            }
            e1 = list3n(local_nptr->sc,local_nptr->dsp,local_nptr);
#if LOCAL_STRUCT_INIT_STATIC 
            local_struct_static(e1); 
#else
            local_offset = local_struct_offset;
            int offset = decl_data_field(type,e1,local_struct_offset);
            if (offset==local_struct_offset) {
                // empty case, let's zero clear now
                int sz = size(type);
                zfill(e1,offset,sz);
                local_struct_offset = offset + sz;
            } else
                local_struct_offset = offset;
#endif
        }
        if (mode==STAT && decl_str_init) gen_delayed_decl_data(e1,local_offset);
        if (init_vars && mode!=LDECL) {
            emit_init_vars();
        }
        if (nptr0==local_nptr) {
            // I have created this, sanitize
            local_nptr = 0;
            local_struct_offset = 0;
        }
        conv->rc_();
        checksym(RC);
        type = t;
        return e1;
    }
    e1=expr13();     //  (type) expr
    if (inmode) { 
        e1 = list4(CAST,e1,t,type);
        type = t;
        return e1;
    } else {
        if (integral(t) || t==DOUBLE || t==FLOAT || t==LONGLONG || t==ULONGLONG)
            e1 = rvalue(e1);    // left value should not be rvalued
        e1 =  correct_type(e1,t);
        type = t;
        return e1;
    }
}

static int
statement_expression()
{
    // statement in expression  (GNU extension)
    //    a = {hoge(); 1;}
    // In docomp, last expression is kept in lastexp.
    //
    int e1;
    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;
    }
    conv->rpar_();
    checksym(RPAR);
    return expr16(e1);
}

/**
 *  basic term
 */
static int
expr14(void)
{
    int e1=0,t,t1;

    switch(sym) {
    case IDENT:
        return expr_identifier();
    case STRINGS:
        e1=list3n(STRINGS,nptr->dsp,nptr);
        type=list3(ARRAY,CHAR,symval);
        getsym(0);
        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();

        if(typeid(sym)) {
            return typecast();
        } else if (sym==LC) {
            return statement_expression();
        } 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 */
    NMTBL *tmp  = 0;
    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 */

        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))) {
            if (tmp) return list2(INDIRECT,(list4(INLINE,e1,arglist,ftype)));
            else return list4(INLINE,e1,arglist,ftype);
        }
    }
    if (tmp) return list2(INDIRECT,list4(stmode==GOTO?CODE:FUNCTION,e1,arglist,ftype));
    else 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;
    }
}

int
types_compatible(int t, int t1)
{
    int eq = 0;
    if (t==t1) eq = 1;
    type = INT;
    return list2(CONST,eq);
}

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


/**
    hash table search
       *char name   
       cheap        heap
       len          length
       hash         hash value
       mode         NONDEF search only, DEF define
    return NMTBL in hash entry
 */
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);
    }
    // we cannot extend hash now
    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) {
        // not found
        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) {
        // newly created entry
        hptr->sc=EMPTY;
    } else {
        // already defined
        cheap = reset_cheap(scheap);
    }
    return hptr;
}

/**
    register general data in hash
      for long long or double
    data is copied into cheap when it is created
        typedef union {
               double d;
               float f;
               int i;
               long l;
               long long ll;
        } Value, *ValuePtr;

 */
extern NMTBL *
get_data(ValuePtr name,int len,int mode)
{
    /* no name copy */
    unsigned int c;
    unsigned int hash0 = 0;
    int i = len;
    char *n = (char*)name;
    struct cheap scheap;
    save_cheap(&scheap,cheap);
    char *top = cheap->ptr;

    while(len-->0) {
        c = *n++;
        hash_value(hash0,*cheap->ptr = c);
        cheap = increment_cheap(cheap,&top);
    }
    return name_space_search(hash_search(top,&scheap,i,hash0,DEF),STRING);
}

extern int
get_data_label(ValuePtr name,int len, void emit(ValuePtr, int, void * ), void *arg)
{
    int lb;
    NMTBL * n = get_data(name,len,DEF);
    if ((lb=attr_value(n,LABEL))) return lb;
    lb=fwdlabel();
    set_attr(n,LABEL,lb);
    emit(name, lb, arg);
    return lb;
}

/**
   get name from already stored in cheap
 */
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);
}

/**
   get name from input
 */
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 int
getstring(void)
{
    char *name = cheap->ptr;
    int i= 0;
    int c;
    unsigned int hash = 0;
    int strings = 0;
    struct cheap scheap;

    save_cheap(&scheap,cheap);
    do {
        while (ch == '"') {
            in_quote = 1;
            getch();  
            while (ch != '"') {
                if (i>STRSIZE-1) {
                    strings = glist3s(i,strings,name);
                    save_cheap(&scheap,cheap);
                    i = 0;
                    name = cheap->ptr;
                }
                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++;
    if (strings) {
        // too long string is stored in glist3
        strings = glist3s(i,strings,name);
        nptr = get_nptr();
        nptr->dsp = strings = reverse0(strings);
        int j;
        for(j = strings, i = 0; j ; j = cadr(j)) {
            i += car(j);
        }
        symval = i;
        return STRINGS;
    }
    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;
    return STRING;
}

/*
   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;
        }
        if (lp64) 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=='"') {
        return sym= getstring();
    } 
    /* 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 = 0;
    NMTBL *n = 0;

    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
    } // else error ?
    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;
    getline1();
    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 */