view mc-macro.c @ 436:d92786033042 loprtc-div

loprtc
author kono
date Sun, 14 Nov 2004 15:41:05 +0900
parents c29eebf3eaf4
children 818505dd6e1f
line wrap: on
line source

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

#include <stdio.h>
#include "mc.h"
#include "mc-parse.h"
#include "mc-macro.h"
#include "mc-code.h"

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

int in_macro_if = 0;
char *chinput;

static int mconcat=0;

static void macro_define0();
static int macro_args(char **pchptr);
static int macro_function(int macrop,char **pchptr,NMTBL *nptr,int history);
static void local_define(char *macro,char *value);
static int macro_eval(int macrop,char *body0,int history);
static char * mappend(int lists,char **result);
static int macro_processing();

static void
gen_source(char *s)
{
     printf("%s",s);
}

/*
     replace macro term into cheap.
     ## concatenation requires repeated replace.
 */

extern void
macro_expansion(NMTBL *nptrm)
{
    int i = mode;
    int macrop = 0;
    int slfree = lfree;
    char *macropp;
    struct cheap scheap;
    mode = STAT;

    save_cheap(&scheap,cheap);

    if (nptrm->sc == FMACRO) {
        macrop=macro_function(macrop,&chptr,nptrm,0);
    } else {
        macrop=macro_eval(macrop,(char *)car(nptrm->dsp),0);
    }
    cheap = reset_cheap(&scheap);
    macropp = cheap->ptr;
    // we can reset cheap, no page wrap in this case
    mappend(reverse0(macrop),&macropp);
    cheap->ptr[-1] ='\n';
    cheap->ptr[0] =0;
    while (mconcat) {
        // ## re-eval macro
        printf("## %s",macropp);
        mconcat = 0;
        macrop = 0;
        macrop=macro_eval(macrop,macropp,0);
	cheap = reset_cheap(&scheap);
	macropp = cheap->ptr;
	// will not override evaled list
        mappend(reverse0(macrop),&macropp);
	cheap->ptr[-1] ='\n';
	cheap->ptr[0] =0;
    }
    cheap = reset_cheap(&scheap);
    mconcat = 0;
    lfree = slfree;
    if (lsrc && !asmf && nptrm->sc==FMACRO) gen_comment(macropp);
    chptrsave = glist2((int)chptr,chptrsave);
    chsave = glist2(ch,chsave);
    chptr = macropp;
    ch = *chptr++;
    mode = i;
}

/* file inclusion */

/*
    file name concatenation
 */

static char *
expand_file_name(char *path,char *name)
{
    char *p = cheap->ptr;
    if (! *path) return name;
    while(( *cheap->ptr = *path++ )) cheap = increment_cheap(cheap,&p);
    if (cheap->ptr[-1]!='/') {
	*cheap->ptr = '/'; cheap = increment_cheap(cheap,&p);
    }
    while(( *cheap->ptr = *name++ )) cheap = increment_cheap(cheap,&p);
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,&p);
    return p;
}

/*
      get file name
            <name> =>   name
                        current_file_name_dir / name
                        libdir / name
            "name" =>   name
                        current_file_name_dir / name
                        include_path / name
 */

static FILE *
getfname(void)
{
    int i,end='"',err=0;
    char *s,*p,**pp,*name;
    FILE *fp;
    struct cheap scheap;
    name = cheap->ptr;

    getch();
    if(skipspc()=='"') { end = '"';
    } else if (ch=='<') { end = '>';
    } else { error(INCERR); err=1; 
    }
    for(i=0;(getch()!=end && ch!='\n');) {
	*cheap->ptr = ch;
	cheap = increment_cheap(cheap,&name);
    }
    if(ch=='\n') error(INCERR);
    if (err) return filep->fcb;
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,&name);
    save_cheap(&scheap,cheap);
    fp = fopen(name,"r") ;
    if (fp) {
	p = name; 
    } else {
	for(pp=(end=='>'||filep->inc=='>') ?l_include_path:include_path;
		*pp;pp++) {
	    p = expand_file_name(*pp,name);
	    if ((fp = fopen(p,"r"))) break ;
	}
    }
    if(!fp) { error(FILERR); return filep->fcb; }
    copy_current_file_dir(s=p);
    //  File name determined. Dispose extra copies.
    cheap = reset_cheap(&scheap);
    if (p!=name) {
	name = cheap->ptr;
	while((*cheap->ptr = *s++)) cheap = increment_cheap(cheap,&name);
	*cheap->ptr = 0;
	cheap = increment_cheap(cheap,&name);
    }
    (filep+1)->inc = end;
    (filep+1)->name0 = name;
    return ( (filep+1)->fcb = fp );
}

/* line input and conversion */

static int macro_if_depth ;
static int macro_if_current ;
static int macro_if_skip ;

/* there may extra non-terminate comment after #if/#else directive */
/*      #endif / * hoge						   */
/*           */
/*                                                                 */

static int
skip_rest_of_line()
{
    getch();
    do {
	while(ch!='\n'&&ch!='\r') {
	    if (!in_comment) {
		if (ch=='/') {
		    getch();
		    if (ch=='/') in_comment=2;
		    else if (ch=='*') {
			in_comment=1;
		    } else continue;
		}
	    } else if (ch=='*') {
		getch();
		if (ch=='/') {
		    in_comment=0; 
		    return macro_if_skip?0:1;
		}
		else continue;
	    }
	    getch();
	}
	if (in_comment==1) { getline(); getch(); }
    } while(in_comment==1);
    in_comment=0;
    return 0;
}

/*
     getline from chptr or chinput (for internal source)
        with macro processing 
 */

static int next_eof;

extern void
getline(void)
{
    int i;
    int c;

    if (next_eof) {
	next_eof=0;
	error(EOFERR);
    }
    do {
	if (chinput) {
	    if (! *chinput) {
		chinput=0;
		continue;
	    }
	    chptr=linebuf;
	    i=0;
	    while((*chptr++=c=*chinput++)&&(c!='\n')) {
		if (++i > LBUFSIZE-2) error(LNERR);
	    }
	} else {
	    lineno++;
	    glineno++;
	    chptr=linebuf;
	    i=0;
	    while ((*chptr++ = c = getc(filep->fcb)) != '\n') {
		if (++i > LBUFSIZE-2) error(LNERR);
		if (c==EOF) {
		    next_eof=1;
		    --chptr;
		    break;
		}
	    }
	}
	*chptr = '\0';
	if (lsrc && !asmf && !macro_if_skip && linebuf[0]) gen_comment(linebuf);
	if (*(chptr = linebuf) == '#' && !in_comment && !in_quote) {
	    if (macro_processing()) return;
	}
    } while(!in_quote && (macro_if_skip || linebuf[0] == '#'));
}

/* preprocessor directive */

/* line continuation \\ */

extern void
check_macro_eof()
{
    int c;
    // can't be in macro expansion
    for(c=0;c<LBUFSIZE-3&&chptr[c];c++);
    if (c>0&&chptr[c-1]=='\\') {
	return;
    } else if (c>0&&chptr[c-1]=='\n') {
	if (c>0&&chptr[c-2]=='\\') {
	    return;
	} else {
	    c--;
	}
    } 
    chptr[c] = ';';
    chptr[c+1] = '\n';
    chptr[c+2] = 0;
}

/*   #if hoge case */

static void
macro_if()
{
    int i;
    ch= *chptr;
    in_macro_if = 1;
    check_macro_eof();
    getsym(0);
    /* i=cexpr(expr(1)); #if allow undefined symbols.. */
    i=expr(1); 
    in_macro_if = 0;
    if (car(i)==CONST) i=cadr(i);
    else i=0;
    if (ch) {
	if (chptr[-1]==ch) {
	/* we are fall into getch(), which lost the last ch */
	/* chptr[-1]==ch check is fanatic, but ... */
	    chptr--;
	} else error(-1);
    }
    macro_if_depth = macro_if_current;
    macro_if_skip = !i;
}

static int
macro_processing()
{
    int i;
    int c;
    int mode_save;

    ++chptr;
    while (*chptr==' '||*chptr=='\t') ++chptr;
    switch(chptr[0]*chptr[1]) {
    case 'i'*'f':
	if ((macroeq("ifdef") || macroeq("ifndef"))) {
	    c = (chptr[-4]=='n');
	    macro_if_current++;
	    if (!macro_if_skip) {
		mode_save = mode; mode = IFDEF;
		ch= *chptr;
		i = getsym(0);
		mode = mode_save;
		macro_if_depth = macro_if_current;
		macro_if_skip = (!i)^c;
	    }
	    return 0;
	} else if (macroeq("if")) {
	    macro_if_current++;
	    if (!macro_if_skip) {
		macro_if();
	    }
	    return 0;
	}
	break;
    case 'e'*'l':
	if (macroeq("elif")) {
	    if (macro_if_current==0) {
		error(MCERR); /* extra #else */
		return 0;
	    }
	    if (macro_if_current == macro_if_depth) {
		if (!macro_if_skip || macro_if_skip==2) {
		    macro_if_skip=2;
		    return 0;
		}
		macro_if();
	    }
	    return 0;
	} else if (macroeq("else")) {
	    if (macro_if_current==0) {
		error(MCERR); /* extra #else */
		return 0;
	    }
	    if (macro_if_current == macro_if_depth) {
		if (macro_if_skip==2) ;
		else if (macro_if_skip) macro_if_skip=0;
		else macro_if_skip=1;
	    }
	    return skip_rest_of_line();
	}
	break;
    case 'e'*'n':
	if (macroeq("endif")) {
	    if (macro_if_current == macro_if_depth) {
		macro_if_skip = 0;
		macro_if_depth = --macro_if_current;
	    } else {
		if (macro_if_current<=0) {
		    error(MCERR); /* extra #if */
		    return 0;
		}
		macro_if_current--;
	    }
	    return skip_rest_of_line();
	}
    }
    if (macro_if_skip) return 0;
    switch(chptr[0]) {
    case 'd':
	if (macroeq("define")) {
	    ch= *chptr;
	    macro_define0();
	    *(chptr = linebuf) = '\0';
	    return 0;
	}
	break;
    case 'u':
	if (macroeq("undef")) {
	    i=mode;
	    mode=IFDEF;
	    ch= *chptr;
	    if (getsym(0)) {
		if (nptr->sc == MACRO) {
		    nptr->sc = EMPTY;
		} else if (nptr->sc == FMACRO) {
		    nptr->sc = EMPTY;
		    /* we cannot reclaim it's arg */
		} else error(MCERR);
	    }
	    mode=i;
	    return 0;
	}
	break;
    case 'i':
	if (macroeq("include")) {
	    if(filep+1 >= filestack + FILES) error(FILERR);
	    if ( ((filep+1)->fcb=getfname()) == NULL) error(FILERR);
	    (filep+1)->ln=lineno;
	    lineno=0;
	    ++filep;
	    *(chptr = linebuf) = '\0';
	    return 0;
	}
	break;
#if ASM_CODE
    case 'a':
	if (c=='a'&&macroeq("asm")) {
	    if (asmf) error(MCERR);
	    asmf = 1;
	    getline();
	    while (asmf) {
		gen_source(linebuf);
		getline();
	    }
	    return 0;
	}
	break;
    case 'e':
	if (macroeq("endasm")) {
	    if (!asmf) error(MCERR);
	    asmf = 0;
	    return 0;
	}
	break;
#endif
    case ' ': case '\t':
	getline();
	return 0;
    }
    error(MCERR);
    return 0;
}

extern int
macroeq(char *s)
{
    char *p;

    for (p = chptr; *s;) if (*s++ != *p++) return 0;
    chptr = p;
    return 1;
}

/* macro interpreter */

extern void
macro_define(char *macro)
{
    char *chptr_save;
    int chsave;

    chptr_save = chptr;
    chsave = ch;
    chptr = macro;
    ch= *chptr++;
    macro_define0();
    chptr = chptr_save;
    ch = chsave;
}

/* macro define from chptr */

static void
macro_define0()
{
    int i,args,c;
    char **body;

    i=mode;
    mode=MDECL;
    // ch= *chptr; ??
// fprintf(stderr,"macro def: ch %c *chptr %c\n",ch,*chptr);
    getsym(0);
// fprintf(stderr,"macro def: %s =>",name); 
    if (nptr->sc != EMPTY) { /* override existing macro */
    }
    args = 0;
    if (ch=='(') {
	nptr->sc = FMACRO;
	args = macro_args(&chptr);
    } else {
	nptr->sc = MACRO;
	nptr->ty = -1;
    }
    // equal is allowed for -Dhoge=aho option
    if (ch=='=') chptr++;
    while((c=*chptr)==' '||c=='\t') chptr++;
    nptr->dsp = list2((int)cheap->ptr,args); /* macro body */
    body = (char **)&car(nptr->dsp);
    while ((*cheap->ptr = c = *chptr++)
	&& c != '\n') {
	cheap = increment_cheap(cheap,body);
	if (c=='/'&&chptr[0]=='/') {
	    cheap->ptr--; 
	    *cheap->ptr = '\0';
	    while(*chptr++); break;
	} else if (c=='/'&&chptr[0]=='*') {
	    cheap->ptr--; chptr++;
	    while((c = *chptr++)) {
		if (c=='*'&&chptr[0]=='/') {
		    c = *chptr++; break;
		}
	    }
	    if (!c) break;
	} else if (c=='\\' && (*chptr=='\n'||*chptr==0)) {
	    chptr++;
	    cheap->ptr--;
	    getline();
	}
    }
    if (c=='\n') {
	*cheap->ptr = '\0';
    }
    cheap = increment_cheap(cheap,body);
// fprintf(stderr,"%s\n",(char *)car(nptr->dsp));
    mode=i;
}

// create function macro argument list
//    return  list2((char*)arg,next)

static int
macro_args(char **pchptr)
{
    int c;
    int in_quote = 0;
    int in_wquote = 0;
    int plevel = 0;
    char **body;
    char *chptr = *pchptr;
    int args = list2((int)cheap->ptr,0);
    body = (char **)&car(args);
    for(;;) {
        *cheap->ptr = c = *chptr++;
	cheap = increment_cheap(cheap,body);
	if (!c)  {
	    chptr--;
	    error(MCERR);
	    *pchptr = chptr;
	    return reverse0(args);
	}
	if (in_quote) {
	    if (c=='\\') {
		if (*chptr != '\n') {
		    *cheap->ptr = *chptr++;
		    cheap = increment_cheap(cheap,body);
		} else {
		    getline();
		}
	    } else if (c=='\'') {
		in_quote = 0;
	    }
	} else if (in_wquote) {
	    if (c=='\\') {
		if (*chptr !='\n') {
		    *cheap->ptr = *chptr++;
		    cheap = increment_cheap(cheap,body);
		} else {
		    *cheap->ptr = '\n';
		    getline();
		}
	    } else if (c=='"') {
		in_wquote = 0;
	    }
	} else if (c=='"') {
	    in_wquote = 1;
	} else if (c=='\'') {
	    in_quote = 1;
	} if (plevel==0) {
	    if (c==',') {
		cheap->ptr[-1] = 0;
		args = list2((int)cheap->ptr,args);
		body = (char **)&car(args);
	    } else if (c==')') {
		cheap->ptr[-1] = 0;
		break;
	    } else if (c=='(') {
		plevel++;
	    } else if (c=='\\') {
		if (*chptr=='\n') {
		    cheap->ptr--;
		    getline();
		}
//	    } else if (c==' '||c=='\t') {
//		cheap->ptr--;
	    } else if (c=='\n') {
		cheap->ptr--;
		getline();
		chptr = *pchptr;
	    }
	} else if (c==')') {
	    plevel--;
	} else if (c=='(') {
	    plevel++;
	} else if (c=='\n') {
	    cheap->ptr--;
	    getline();
	    chptr = *pchptr;
	}
    }
    *pchptr = chptr;
    return reverse0(args);
}

/* output macro expansion result into macrobuf (macropp) */

static int
macro_function(int macrop,char **pchptr,NMTBL *nptr,int history)
{
    int args,sargs,values,evalues;
    char *macro;

    sargs = args = cadr(nptr->dsp);
    values = macro_args(pchptr);
    if (pchptr==&chptr) {
	ch = *chptr++;
    }
    evalues = 0;
    while(values) {
	evalues = list2(macro_eval(0,(char *)car(values),history),evalues);
	values = cadr(values);
    }
    evalues = reverse0(evalues);
    enter_scope();
    while(args) {
	mappend(reverse0(car(evalues)),&macro);
	local_define((char *)car(args),macro);
	args = cadr(args);
	evalues = cadr(evalues);
    }
    macro = (char *)car(nptr->dsp);
    macrop = macro_eval(macrop,macro,list2((int)macro,history));
    args = sargs;
    leave_scope();
    return macrop;
}

static void
local_define(char *macro,char *value)
{
    NMTBL *nptr0,*nlist;
    while(*macro==' '||*macro=='\t') macro++;
    nptr0 = name_space_search(nlist=get_name(macro,0,DEF),MACRO);
    nptr0 = make_local_scope(nlist,nptr0,MACRO);
    nptr0->nm = value;
}

/*
    Evaluate macro string.
    reuslt:   list2("replaced string",next)
 */

static int
macro_eval(int macrop,char *body0,int history)
{
    int c,len;
    int in_quote = 0;
    int in_wquote = 0;
    char *macro;
    char *body = body0;
    char **expand;
    NMTBL *nptrm;
    macrop = list2((int)cheap->ptr,macrop);
    expand = (char **)&car(macrop);
    for(; (c = *body++) ;) {
	if (in_quote) {
	    if (c=='\\') {
		*cheap->ptr = c; c = *body++;
		cheap = increment_cheap(cheap,expand);
	    } else if (c=='\'') {
		in_quote = 0;
	    }
	} else if (in_wquote) {
	    if (c=='\\') {
		*cheap->ptr = c; c = *body++;
		cheap = increment_cheap(cheap,expand);
	    } else if (c=='"') {
		in_wquote = 0;
	    }
	} else if (c=='"') {
	    in_wquote = 1;
	} else if (c=='\'') {
	    in_quote = 1;
	} else if (c=='#' && *body=='#') {
	    // name concatenation. skip ## and re-eval macro line.
	    mconcat = 1; body++; continue;
	} else if (alpha(c)) {
	    body--; // ungetc
	    nptrm = get_name(body,&len,NONDEF);
	    if (!nptrm) {
		while((*cheap->ptr = *body++) && len--)
		    cheap = increment_cheap(cheap,expand);
		body--;
		continue;
	    }
	    body += len;
	    c = *body;
	    nptrm = name_space_search(nptrm,MACRO);
	    macro = (char *)car(nptrm->dsp);
	    switch(nptrm->sc) {
	    case FMACRO:
		if (c==' '||c=='\t') {
		    while (c==' '||c=='\t') c=*body++;
		    body--;
		}
		if(c!='(') error(MCERR);
		*cheap->ptr = 0;
		cheap = increment_cheap(cheap,expand);
		body++;
		macrop = macro_function(macrop,&body,nptrm,
			list2((int)macro,history));
		macrop = list2((int)cheap->ptr,macrop);
		expand = (char **)&(car(macrop));
		break;
	    case MACRO:
		if (neqname(nptrm->nm,macro)) {
		    if (macro[0]==0)  continue;
		    *cheap->ptr = 0;
		    cheap = increment_cheap(cheap,expand);
		    macrop=macro_eval(macrop,macro,list2((int)macro,history));
		    macrop = list2((int)cheap->ptr,macrop);
		    expand = (char **)&(car(macrop));
		    break;
		}
	    default:
		macro = nptrm->nm;
	    case LMACRO:
		while((*cheap->ptr = *macro++)/* && len-- */)
		    cheap = increment_cheap(cheap,expand);
	    }
	    continue;
	}
	*cheap->ptr = c;
	cheap = increment_cheap(cheap,expand);
    }
    *cheap->ptr = 0;
    cheap = increment_cheap(cheap,expand);
    return macrop;
}

/*
    cancat list2("string",next) into cheap.
    result overwrited by next cheap allocation
 */

static char *
mappend(int lists,char **result)
{
    char *p;
    *result = cheap->ptr;
    for(;lists;lists = cadr(lists)) {
        p = (char *)car(lists);
        for(;(*cheap->ptr=*p++);cheap = increment_cheap(cheap,0)) {
	    // in_quote + \n case ? should be \n.
	    if (p[-1]=='\n') cheap->ptr[0]=' ';
	}
    }
    cheap = increment_cheap(cheap,0);
    return *result;
}

/* end */