#include #include #include #include #include #include #include #include #include "lexc.h" #include "lexc.m" #define OPT_PROFILE 0 extern int filesrc_noline; static void near preproc_error ( const char *line, const char *pos, const char *msg) { char buf[100]; snprintf (buf,sizeof(buf)-1,MSG_B(E_IVLDDEF ,"Définition invalide : %s\n%%s\n%%*s\n" ,"Invalid definition : %s\n%%s\n%%*s\n") ,msg); lexcerr_bprintf (buf,line,(int)(pos-line),"^"); } /* #Spécification: Préprocesseur / macros / limites Nombre maximum de macros: 5000 */ #define MAXSYM_EXT 100 static struct{ /* Les tbsym sont alloues par section discontinues */ DEF_SYM *tbsym[MAXSYM_EXT]; /* Ca fait au maximum 5000 macros */ int nb; } tbx; /* Table de pointeur pour paramètre de macros Cette table est alloué en section au besoin */ #define MAX_TBPRM 1000 /* Alloue 1000 pointeur à la fois */ typedef struct _tbprm { int nbprm; struct _tbprm *next; char *tbprm[MAX_TBPRM]; } L_TBPRM; static L_TBPRM *ltbprm; /* Section courante */ static DEF_SYM *tbsym; static short nbsym; /* Nombre de symbole couramment dans tbsym */ /* Ne tient pas compte des tbx.tbsym[] déjà */ /* rempli */ #define MAXSYM 500 /* Nombre maximum de symbole dans tbsym */ static short global=0; /* Nombre de symboles définis sur la ligne de commande */ static char *alphakey; /* Permet de savoir rapidement si un symbole existe */ static DEF_SYM **hashkey;/* Index hashing */ #define MAX_HASHKEY 512 /* Puissance de 2 */ #define LIMIT_HASH(h) h &= (MAX_HASHKEY - 1) #if OPT_PROFILE int nblocate=0; /* Nombre d'appel preproc_locate */ int nbessai=0; /* Nombre d'iteration */ #define profile_inc(var) var++; static void profile_print(){ printf ("Preproc_locate %d %d %d %d\n",nblocate,nbessai,tbx.nb,nbsym); } static void profile_init(){ atexit(profile_print); } #else #define profile_inc(var) #define profile_init() #endif /* Insere un symbole dans la table de hashing */ static void near preproc_insertsym (DEF_SYM *ptsym) { char *nom = ptsym->nomdef; int hash = 0; alphakey[*nom] = 1; while (*nom != '\0') hash += *nom++; LIMIT_HASH(hash); ptsym->nextsym = hashkey[hash]; hashkey[hash] = ptsym; } /* Initialise ou réinitialise la table de symbole des macros */ void preproc_init (void) { if (tbsym != NULL){ int offset = global; int i; for (i=0; inbprm; j++) free (ptsym->tbprm[j]); free (ptsym->nomdef); free (ptsym->repl); } offset = 0; } /* Libère les extensions de tbsyms */ for (i=1; inext != NULL){ L_TBPRM *next = ltbprm->next; free (ltbprm); ltbprm = next; } /* Replace l'allocateur dans ltbprm */ if (global > 0){ DEF_SYM *ptsym = tbsym + global -1; int nb = ptsym->nbprm; if (nb < 0) nb = 0; ltbprm->nbprm = (int)(ptsym->tbprm - ltbprm->tbprm) + nb; }else{ ltbprm->nbprm = 0; } }else{ tbsym = (DEF_SYM*) malloc_err ((unsigned)MAXSYM*sizeof(DEF_SYM),1); tbx.tbsym[0] = tbsym; tbx.nb = 1; hashkey = (DEF_SYM**) malloc_err(MAX_HASHKEY*sizeof(DEF_SYM*),1); alphakey = (char *) malloc_err(256*sizeof(char),1); ltbprm = (L_TBPRM*) malloc_err (sizeof(L_TBPRM),1); ltbprm->next = NULL; ltbprm->nbprm = 0; profile_init(); } nbsym = global; { /* Reconstruit les index d'acces */ short i; DEF_SYM *ptsym = tbsym; for (i=0; inomdef)==0){ ret = ptsym; break; }else{ ptsym = ptsym->nextsym; } } } return ret; } static char *preproc_declocate=NULL; /* Enregistre le nom de la macro dont on veut connaitre l'origine. On utilise cette fonction pour affiché l'endroit exacte où une macro a été déclaré. (quel source, inclue par quel autre, etc ...) */ void preproc_setlocate(const char *_locate) { if (preproc_declocate != NULL) free (preproc_declocate); preproc_declocate = strdup_err (_locate,1); } /* Enregistre une macro son remplacement */ /* #Spécification: Préprocesseur/ macros/ paramètres Une macro avec 0 paramètre s'utilise différemment d'une macro sans paramètres. #define mac() ... et #define mac ... ne s'utilise pas de la même façon. */ void preproc_putdef ( const char *nomdef, /* nom de la macro */ const char *repl, /* Chaine de remplacement */ int lenrepl, char *tbprm[], /* Paramètres */ int nbprm, /* Nombre de paramètres */ /* -1 pour macro sans paramètre */ /* Différent de macro avec 0 paramètre */ int cmdline) /* provient de la ligne de commande ou pas */ { DEF_SYM *ptsym; comment_init(); /* Quand on voit un #define, on reset */ /* l'accumulation des commentaires */ /* Les fichier .m font deborder bufcom */ if (preproc_declocate != NULL && strcmp(nomdef,preproc_declocate)==0){ /* La macro qu'on enregistre interesse l'usager, On doit lui indiqué où la déclaration s'est faite. */ fprintf (stderr,MSG_B(I_DEFINEHERE ,"Macro %s déclarée ici\n" ,"Macro %s defined here\n"),preproc_declocate); filesrc_showpos (stderr); } if (tbsym == NULL){ preproc_init(); }else if (nbsym == MAXSYM){ if (tbx.nb == MAXSYM_EXT){ fprintf (stderr,MSG_B(E_LIMITCPP ,"Limite du préprocesseur: Trop de macros\n" "\tMaximum %d\n" ,"Preprocessor limit: Too many defines\n" "\tMaximum %d\n") ,MAXSYM_EXT*MAXSYM); exit (-1); }else{ /* Vérifie si la table doit être grossie */ tbsym = (DEF_SYM*)malloc_err((unsigned)MAXSYM*sizeof(DEF_SYM),1); tbx.tbsym[tbx.nb++] = tbsym; nbsym = 0; } } if (cmdline){ /* Les macros de la ligne de commande doivent être les premiers De la liste. On réinitialise la liste en eliminant tous les autres. Ce ne peut pas se produire durant l'interprétation d'un source. */ preproc_init(); global++; } ptsym = tbsym+nbsym; ptsym->nomdef = strdup_err (nomdef,1); ptsym->repl = (char*)malloc_err(lenrepl+1,1); if (lenrepl > 0) memmove (ptsym->repl,repl,lenrepl); ptsym->repl[lenrepl] = '\0'; ptsym->lenrepl= lenrepl; ptsym->nbprm = (short)nbprm; ptsym->active = 1; if (nbprm > 0){ /* Vérifie s'il y a de la place dans la table courante */ int nbtb = ltbprm->nbprm; if (nbtb + nbprm >= MAX_TBPRM){ /* Tous les pointeurs de ltbprm ne sont pas occupés */ L_TBPRM *newtb = (L_TBPRM*)malloc_err (sizeof(L_TBPRM),1); newtb->next = ltbprm; ltbprm = newtb; nbtb = 0; } ptsym->tbprm = <bprm->tbprm[nbtb]; ltbprm->nbprm = nbtb + nbprm; memmove (ptsym->tbprm,tbprm,nbprm*sizeof(char*)); }else{ /* Lors de réinitialisation, on assume que tous les macros possède un pointeur valide, meme si nbprm = 0. */ ptsym->tbprm = <bprm->tbprm[ltbprm->nbprm]; } preproc_insertsym (ptsym); nbsym++; } /* Enregistrement de la déclaration d'un define Retourne position de traitement dans la ligne suivante car peut sauter des commentaires Le texte de remplacement sera minimalement traité. S'il y a des commentaires imbriqués dans le texte, il sera conservé. La fonction preproc_skipline permet de localiser la fin de la ligne avant le dernier commentaire (potentiellement étendue sur plusieurs lignes). La fonction charge alors une nouvelle ligne dans line, c'est pourquoi il faut faire une copie temporaire dans repl. Les valeurs debpt et endpt serve uniquement à identifié la partie valide de repl. */ char *preproc_recdefine( char *line, /* pointe au début de la ligne #define */ /* line est aussi le buffer unique de ligne */ char *pt) { static char *repl=NULL; if (repl == NULL) repl=(char*)malloc_err(3000,1); if (token_idok(*pt)){ /* #Specification: preprocesseur / macro Longueur maximum d'un nom de macro: 99 caractères. */ char nomdef[100]; pt = token_copyid (pt,nomdef); if (*pt == '('){ /* macro avec paramètres */ /* #Specification: preprocesseur / limites Nombre maximum de paramètres pour un macro: 30 */ int nbprm=0; char *tbprm[30]; while (1) { char id[100]; pt++; /* saute ( ou , */ pt = str_skip (pt); if (token_idok(*pt)){ if (nbprm == 30){ preproc_error (line,pt,MSG_B(E_TOOMANYPARM ,"Trop de paramètre, maximum 30" ,"Too many parameters, max 30")); precond_skipline(&pt,line); break; }else{ pt = token_copyid (pt,id); tbprm[nbprm++] = strdup_err(id,1); pt = str_skip (pt); } } if (*pt != ','){ if (*pt == ')'){ char *debpt = pt = str_skip (pt+1); strcpy (repl,debpt); { char *endpt = precond_skipline (&pt,line); int len = (int)(endpt-debpt); repl[len] = '\0'; preproc_putdef (nomdef,repl,len,tbprm,nbprm,0); } }else{ preproc_error (line,pt,MSG_B(E_MISSPAR ,"Parenthèse manquante" ,"Missing parenthesis")); precond_skipline(&pt,line); } break; } } }else if (isspace(*pt) || *pt == '\0'){ /* macro ordinaire */ char *debpt = pt = str_skip (pt); strcpy (repl,debpt); { char *endpt = precond_skipline (&pt,line); int len = (int)(endpt-debpt); repl[len] = '\0'; preproc_putdef (nomdef,repl,len,(char**)NULL,-1,0); } }else{ preproc_error (line,pt,""); precond_skipline(&pt,line); } }else{ preproc_error (line,pt,MSG_B(E_IVLDSYM ,"Symbole invalide","Invalid symbol")); precond_skipline(&pt,line); } return (pt); } #if 0 static char sigerr; /* Signale les erreurs de unref */ /* Enregistre si on doit signaler des erreurs de undef ou pas */ void preproc_errundef (int outputerr) { sigerr = (char)outputerr; } #endif /* Elimine la déclaration d'un define Retourne adresse de lecture dans la ligne */ char *preproc_undef( char *DUM1, /* pointe au début de la ligne #undef */ char *ptl) { char txt[100]; ptl = token_copyid (ptl,txt); if (txt[0] != '\0'){ DEF_SYM *ptsym = preproc_locate (txt); if (ptsym != NULL){ ptsym->nomdef[0] = '\0'; #if 0 }else if (sigerr){ lexcerr_bprintf ("#undef : Symbole %s n'est pas définie\n" ,"#undef : Undefined symbol %s\n",txt); #endif } } return (ptl); } static int macro_replnb=0; /* Remet le compteur de remplacement macro a 0. Cette fonction est generalement appelée par le module de lecture de ligne (filesrc.c). */ void preproc_resetmacronb() { macro_replnb = 0; } static struct PTSYM_ACT{ DEF_SYM *ptsym; char nouvel_etat; }tbact[40]; static PTSYM_ACT *ptsym_act = tbact; /* Rend effectif les modifications de preproc_macroact */ void preproc_enableact() { PTSYM_ACT *pt = tbact; while (pt < ptsym_act){ pt->ptsym->active = pt->nouvel_etat; pt++; } ptsym_act = tbact; } /* Active/Désactive une macro à partir d'un token inséré par preproc_convert() Retourne un pointeur suivant le token. */ char *preproc_macroact (char *ptl) { DEF_SYM *ptsym; assert ((ptsym_act-tbact)<40); memcpy (&ptsym,ptl+1,sizeof(ptsym)); ptsym_act->ptsym = ptsym; ptsym_act->nouvel_etat = (char)(ptl[0] == MACRO_ACTIVE_CAR); ptsym_act++; ptl += sizeof(ptsym)+1; return ptl; } /* Fait le remplacement d'un symbole si c'est un define Retourne 1 si le token a été remplacé, 0 sinon Si le token était un define, la chaine de remplacement a été inséré dans le buffer de traitement (line). Ca cause une faiblesse dans l'affichage de message d'erreur. Le numéro de colonne est faussé. Vérifie l'utilisation récursive d'un symbole */ int preproc_convert ( TOKEN *tok, /* token à convertir si requis */ char **ptl, /* position de lecture dans la ligne */ char *line) /* buffer de lecture */ { int ret = 0; char *txt = token_txt (tok); char *repl = NULL; char buf[MAXSIZ_PATH]; int lenrepl=-1; { DEF_SYM *ptsym = preproc_locate (txt); if (ptsym != NULL){ /* #Spécification: macro / recursion / stratégie La récursivité est permise dans les définitions de macro du préprocesseur. En fait, la définition d'une macro est désactivé lors de l'expansion de celle-ci. L'exemple suivant illustre la situation. #define macro 1 + macro + 1 L'expression "a = macro" produira "a = 1 + macro + 1" Ce mécanisme est utile pour redéfinir partiellement une fonction: #define fct(p1,p2) printf ("fct appelé\n"); fct(p1,p2) **** Ce n'est pas encore supporté dans ce préprocesseur **** Ca s'en vient (21/04/95) */ if (macro_replnb >= 300){ if (macro_replnb == 300){ lexcerr_bprintf ( "Ligne %d trop complexe,\n" "\tprobablement une définition de macro récursive\n" ,"Line %d too complex\n" "probably a recursive macro definition\n" ,filesrc_curline()); } macro_replnb++; }else{ macro_replnb++; /* #Spécification: macro / recursion / stratégie **** C'est nul **** Un autre technique qui est utilisé c'est en identifiant sur quel ligne et dans quelle portion de cette ligne il y a eu remplacement sur la macro. Soit la macro X = ABXCD et la sequence XYZ XYZ devient ABXCDYZ ^ -----| Lors du premier remplacement de X, on note sur quel ligne, on fait le remplacement et on note la position du premier caractère après le remplacement. Si on voit X une autre fois avant d'avoir atteint la position Y, on sait que l'usage est recursif. Attention: Cette methode est pas valide a 100% Ca desactive l'usage de la macro dans la zone de remplacement et une sequence comme suit devient invalide. #define max(x,y) ((x)<(y) ? (y) : (x)) max (val, max (va2,yy)); Le but n'est pas reellement de detecter les recursions parce que ANSI dit que: #define x x+1 est tout a fait valide. Durant le remplacement la macro est tout simplement desamorce. Ca permet de faire des chose comme: #define fct printf("Appelle fct"); fct */ /* #Spécification: macro / recursion / stratégie 2 Voici une nouvelle approche. Lorsqu'on traite une macro, on fait le remplacement et on retraite le résultat. A l'intérieur du résultat on va placer des tokens spéciaux indiquant à quel moment la macro doit être activé et désactivé comme ceci. #define M(a,b) allo a comment b M aussi Soit le token $M indiquant que la macro M doit être activé. Soit le token %M indiquant que la macro M doit être déactivé. Le remplacement donnera ceci pour l'expression M(toto,titi). # %M allo $M toto %M comment $M titi %M M aussi $M ---- ---- ------- # La macro M sera active uniquement durant la partie sousligne. L'expression suivante fonctionnera alors tres bien. M(allo,M(x,y)) $M est en fait \rp ou p est un pointeur. %M est \np. On choisit ces caractères parce qu'il sont filtré par filesrc.c. */ if (ptsym->active){ if (ptsym->nbprm != -1){ /* Macro avec paramètre, il faut localiser les paramètres */ repl = preproc_expand (line,ptl,ptsym,lenrepl); }else{ lenrepl = ptsym->lenrepl; repl = ptsym->repl; } // ******* bug ******* //ptsym->lastline = -1; // filesrc_noline; //ptsym->lastptl = *ptl + lenrepl; if (lenrepl != -1){ if (lenrepl > 0){ char *pt = *ptl; pt-=sizeof(ptsym)+1; *pt = MACRO_ACTIVE_CAR; memcpy (pt+1,&ptsym,sizeof(ptsym)); pt -= lenrepl; memcpy (pt,repl,lenrepl); *ptl = pt; ptsym->active = 0; } ret = 1; } } } }else if (txt[0] == '_' && txt[1] == '_'){ /* On test ces cas la apres, parce qu'il arrive rarement */ if (strcmp(txt,"__LINE__")==0){ lenrepl = sprintf (buf,"%d",filesrc_curline()); repl = buf; }else if (strcmp(txt,"__FILE__")==0){ char fname[MAXSIZ_NAME]; path_splitlex (filesrc_curfile(),NULL,fname); lenrepl = sprintf (buf,"\"%s\"",fname); repl = buf; }else if (strcmp(txt,"__DATE__")==0){ char datestr[15]; date_u2a(date_currentu(),datestr); lenrepl = sprintf (buf,"\"%s\"",datestr); repl = buf; }else if (strcmp(txt,"__TIME__")==0){ char timestr[15]; time_u2a(time_currentu(),timestr); lenrepl = sprintf (buf,"\"%s\"",timestr); repl = buf; } if (lenrepl != -1){ if (lenrepl > 0){ #if 0 int len = strlen(*ptl); char *pt = *ptl; memmove (pt+lenrepl,pt,len+1); /* Copie aussi le '\0' */ memcpy (pt,repl,lenrepl); #else *ptl -= lenrepl; char *pt = *ptl; memcpy (pt,repl,lenrepl); #endif } ret = 1; } } } return ret; } /* Affiche la déclaration de la toutes les macros déjà enregistré */ void preproc_dumpmac (FILE *fout) { // Les macros définis sur la ligne de commande ne sont // pas extrait. int offset = global; for (int i=0; inomdef[0] != '\0'){ fprintf (fout,"#define %s",ptsym->nomdef); if (ptsym->nbprm >= 0){ const char *ctrl = "%s"; fputs ("(",fout); for (int p=0; pnbprm; p++){ fprintf (fout,ctrl,ptsym->tbprm[p]); ctrl = ",%s"; } fputs (") ",fout); } char *repl = ptsym->repl; fputs ("\t",fout); // Génère la définition sans créer des lignes trop longues while (1){ char *pt = repl; int len; do { while (*pt > ' ') pt++; while (isspace(*pt)) pt++; len = (int)(pt-repl); }while (len < 80 && *pt != '\0'); if (len > 0) fwrite (repl,1,len,fout); if (*pt == '\0'){ fputs ("\n",fout); break; }else{ fputs ("\\\n",fout); repl = pt; } } } } } }