#include #include #include #include #include #include "lexc.h" extern int filesrc_noline; static int eol; static char *ptl; static char *buflin; static TOKEN tok; static int near evaltrien (void); /* Lit le token suivant S'assure de ne pas lire de token sur la ligne suivante Retourne != 0 si un token lue, 0 si pas de token */ static int near nextitem (void) { if (!eol){ if (precond_skipcom(&ptl,buflin) && *ptl != '\0'){ /* il y a un token a lire */ if (*ptl == '#'){ ptl++; tok.type = TOK_TICTACTO; }else if (token_get0(&tok,&ptl,buflin,1,0)==-1){ /* fin de ligne rencontré sans token */ eol = 1; } }else{ eol = 1; } } return (!eol); } /* Lit un opérande et évalue Retourne 0 si faux, 1 si vrai Lit toujours un opérande de plus dans tok Retourne 0 par defaut si aucun item */ static int near evalelm (void) { int val = 0; if (nextitem()){ int gonext = 1; if (tok.type == TOK_ID){ if (strcmp(tok.longtxt,"defined")==0){ /* #Specification: Preprocesseur / condition Le préprocesseur accepte les deux syntaxes suivantes: # #if defined(symbole) #if defined symbole # */ /* Coupe temporairement operation du preprocesseur. On ne doit pas faire de remplacement macro puisqu'on veut identifier la macro elle-même. On s'attend soit à une parenthèse, soit à un symbole. */ int ok = 0; int old = token_setpreproc(0); if (nextitem() && !eol){ if (tok.type == TOK_OPNPAR){ int ret = nextitem(); if (ret && (tok.type == TOK_ID || tok.type == TOK_KEYWORD)){ val = preproc_locate (tok.longtxt) != NULL; nextitem (); if (!eol && tok.type == TOK_CLSPAR){ ok = 1; } } }else if (tok.type == TOK_ID || tok.type == TOK_KEYWORD){ val = preproc_locate (tok.longtxt) != NULL; ok = 1; } } token_setpreproc(old); if (!ok){ lexcerr_bprintf ( "Erreur de syntaxe : Operateur \"defined\"\n" ,"Syntax error : \"defined\" operator\n"); } }else{ /* Un symbole non définis est remplacé par 0 */ val = 0; } }else if (tok.type == TOK_CTENUM){ val = (int)token_evalnum(&tok); }else if (tok.type == TOK_QUOTE){ val = token_evalquote(&tok); }else if (tok.type == TOK_OPNPAR){ val = evaltrien (); if (tok.type != TOK_CLSPAR){ lexcerr_bprintf ("Manque une parenthèse\n" ,"Missing parenthesis\n"); } }else if (tok.type == TOK_UNOPER && tok.type2.unoper == UNOPER_BOOLNOT){ val = !evalelm(); gonext = 0; }else if (tok.type == TOK_UNOPER && tok.type2.unoper == UNOPER_BITNOT){ val = ~evalelm(); gonext = 0; }else if (tok.type == TOK_BINOPER && tok.type2.unoper == BINOPER_MOINS){ val = -evalelm(); gonext = 0; }else if (tok.type == TOK_BINOPER && tok.type2.unoper == BINOPER_PLUS){ val = evalelm(); gonext = 0; }else if (tok.type == TOK_TICTACTO){ /* #Spécification: préprocesseur / condition / macro texte Un syntaxe particulière permet de vérifier le contenu d'une macro contenant une chaine. On place un # devant le nom de la macro suivi de la valeur à tester entre parenthèse. Cet expression retourne 1 si la macro vaut ce qu'il y a entre parenthèse. Cette syntaxe a été vue dans Solaris 2.1 et semble être comprise par GNU CPP. Borland ne supporte pas cette syntaxe. */ int old = token_setpreproc(0); val = 0; if (nextitem() && tok.type == TOK_ID){ DEF_SYM *sym = preproc_locate (tok.longtxt); token_setpreproc(old); int ok = 0; if (nextitem() && tok.type == TOK_OPNPAR && nextitem() && tok.type == TOK_ID){ val = sym != NULL && strcmp(sym->repl,tok.longtxt)==0; ok = nextitem() && tok.type == TOK_CLSPAR; } if (!ok){ lexcerr_bprintf ( "Expression #id(val) invalide\n" ,"Invalid #id(val) expression\n"); } }else{ lexcerr_bprintf ( "Opérateur # doit être suivi d'un identificateur\n" ,"# operator must be followed by an identificator\n"); } token_setpreproc(old); }else{ lexcerr_bprintf ( "Caractère invalide\n%s\n" ,"Invalid character\n%s\n",buflin); } if (gonext) nextitem (); }else{ lexcerr_bprintf ( "Expression incomplète\n%s\n" ,"Incomplete expression\n%s\n",buflin); } return (val); } /* Evalue une série de multiplication, division, modulo Retourne 0 si faux, != 0 si vrai */ static int near evalmult (void) { int val = evalelm (); while (!eol && (tok.type == TOK_BINOPER || tok.type == TOK_STAR)){ if (tok.type == TOK_STAR){ val = val * evalelm(); }else if (tok.type2.binoper == BINOPER_DIVIS){ val = val / evalelm(); }else if (tok.type2.binoper == BINOPER_MODULO){ val = val % evalelm(); }else{ break; } } return (val); } /* Evalue une série de d'addition ou de soustraction Retourne 0 si faux, != 0 si vrai */ static int near evalplus (void) { int val = evalmult (); while (!eol && tok.type == TOK_BINOPER){ if (tok.type2.binoper == BINOPER_PLUS){ val = val + evalmult(); }else if (tok.type2.binoper == BINOPER_MOINS){ val = val - evalmult(); }else{ break; } } return (val); } /* Evalue une série de shift Retourne 0 si faux, != 0 si vrai */ static int near evalshift (void) { int val = evalplus (); while (!eol && tok.type == TOK_BINOPER){ if (tok.type2.binoper == BINOPER_SHIFTL){ val = val << evalplus(); }else if (tok.type2.binoper == BINOPER_SHIFTR){ val = val >> evalplus(); }else{ break; } } return (val); } /* Evalue une série de comparaison Retourne 0 si faux, != 0 si vrai */ static int near evalcmp (void) { int val = evalshift (); while (!eol && tok.type == TOK_BINOPER){ if (tok.type2.binoper == BINOPER_PETIT){ val = val < evalshift(); }else if (tok.type2.binoper == BINOPER_EPETIT){ val = val <= evalshift(); }else if (tok.type2.binoper == BINOPER_GRAND){ val = val > evalshift(); }else if (tok.type2.binoper == BINOPER_EGRAND){ val = val >= evalshift(); }else{ break; } } return (val); } /* Evalue une série de == ou != Retourne 0 si faux, != 0 si vrai */ static int near evalegal (void) { int val = evalcmp (); while (!eol && tok.type == TOK_BINOPER){ if (tok.type2.binoper == BINOPER_EGAL){ val = val == evalcmp(); }else if (tok.type2.binoper == BINOPER_DIFFER){ val = val != evalcmp(); }else{ break; } } return (val); } /* Evalue une série de & Retourne 0 si faux, != 0 si vrai */ static int near evalband (void) { int val = evalegal (); while (!eol && tok.type == TOK_BINOPER && tok.type2.binoper == BINOPER_BITAND){ val = val & evalegal(); } return (val); } /* Evalue une série de ^ Retourne 0 si faux, != 0 si vrai */ static int near evalbxor (void) { int val = evalband (); while (!eol && tok.type == TOK_BINOPER && tok.type2.binoper == BINOPER_BITXOR){ val = val ^ evalband(); } return (val); } /* Evalue une série de | Retourne 0 si faux, != 0 si vrai */ static int near evalbor (void) { int val = evalbxor (); while (!eol && tok.type == TOK_BINOPER && tok.type2.binoper == BINOPER_BITOR){ val = val | evalbxor(); } return (val); } /* Evalue une série de && Retourne 0 si faux, != 0 si vrai */ static int near evaland (void) { int val = evalbor (); while (!eol && tok.type == TOK_BINOPER && tok.type2.binoper == BINOPER_BOOLAND){ int tmp = evalbor(); val = val && tmp; } return (val); } static int near evalor (void) { int val = evaland (); while (!eol && tok.type == TOK_BINOPER && tok.type2.binoper == BINOPER_BOOLOR){ int tmp = evaland(); val = val || tmp; } return (val); } /* Evalue une operateur logique ternaire ??? */ static int near evaltrien (void) { int val = evalor (); if (tok.type == TOK_BINOPER && tok.type2.binoper == BINOPER_TRIEN){ int val1 = evalor(); if (tok.type == TOK_2PT){ int val2 = evalor(); val = val ? val1 : val2; }else{ lexcerr_bprintf("Expression conditionnelle invalide\n" ,"Invalid conditional expression\n"); } } return (val); } /* Evalue un expression logique pour enoncé #if ou #elif Retourne 0 si faux, !=0 si vrai Supporte les || et && et ! et defined NOTE : Les commentaires sont permis dans les expressions Durant l'évaluation le remplacement macro devient disponible s'il ne l'était pas déjà */ int precond_eval (char **pptl,char *line) { int preproc = token_setpreproc (1); int ret; ptl = *pptl; buflin = line; eol = 0; ret = evaltrien(); if (!eol){ lexcerr_bprintf ("Expression invalide\n%s\n" ,"Invalid expression\n%s\n",line); } *pptl = ptl; token_setpreproc (preproc); return (ret); } #ifdef TEST #include static void tst (const char *expr) { char line[200]; char *ptl = line; strcpy (line,expr); printf ("##### expr = :%s:\n",line); printf ("-----> %d\n",precond_eval(&ptl,line)); } void main (void) { printf ("-------- Sans erreur ----------\n"); tst ("1 && /* comm */ 0"); tst ("0 && 1 || 1"); tst ("0 && (1 || 1)"); tst ("0"); tst ("1 || 0"); tst ("1 + 1"); tst ("1 + -2"); tst ("4 * -2"); printf ("-------- Avec erreur ----------\n"); tst (""); tst ("1 &&"); tst ("1 && ("); tst ("1 && (1"); } #endif