/* * Wrc preprocessor syntax analysis * * Copyright 1999-2000 Bertho A. Stultiens (BS) * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * 24-Apr-2000 BS Restructured the lot to fit the new scanner * and reintegrate into the wine-tree. * 01-Jan-2000 BS FIXME: win16 preprocessor calculates with * 16 bit ints and overflows...? * 26-Dec-1999 BS Started this file * */ %{ #include "config.h" #include "wine/port.h" #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <assert.h> #include <ctype.h> #include <string.h> #include "wpp_private.h" #define UNARY_OP(r, v, OP) \ switch(v.type) \ { \ case cv_sint: r.val.si = OP v.val.si; break; \ case cv_uint: r.val.ui = OP v.val.ui; break; \ case cv_slong: r.val.sl = OP v.val.sl; break; \ case cv_ulong: r.val.ul = OP v.val.ul; break; \ case cv_sll: r.val.sll = OP v.val.sll; break; \ case cv_ull: r.val.ull = OP v.val.ull; break; \ } #define cv_signed(v) ((v.type & FLAG_SIGNED) != 0) #define BIN_OP_INT(r, v1, v2, OP) \ r.type = v1.type; \ if(cv_signed(v1) && cv_signed(v2)) \ r.val.si = v1.val.si OP v2.val.si; \ else if(cv_signed(v1) && !cv_signed(v2)) \ r.val.si = v1.val.si OP v2.val.ui; \ else if(!cv_signed(v1) && cv_signed(v2)) \ r.val.ui = v1.val.ui OP v2.val.si; \ else \ r.val.ui = v1.val.ui OP v2.val.ui; #define BIN_OP_LONG(r, v1, v2, OP) \ r.type = v1.type; \ if(cv_signed(v1) && cv_signed(v2)) \ r.val.sl = v1.val.sl OP v2.val.sl; \ else if(cv_signed(v1) && !cv_signed(v2)) \ r.val.sl = v1.val.sl OP v2.val.ul; \ else if(!cv_signed(v1) && cv_signed(v2)) \ r.val.ul = v1.val.ul OP v2.val.sl; \ else \ r.val.ul = v1.val.ul OP v2.val.ul; #define BIN_OP_LONGLONG(r, v1, v2, OP) \ r.type = v1.type; \ if(cv_signed(v1) && cv_signed(v2)) \ r.val.sll = v1.val.sll OP v2.val.sll; \ else if(cv_signed(v1) && !cv_signed(v2)) \ r.val.sll = v1.val.sll OP v2.val.ull; \ else if(!cv_signed(v1) && cv_signed(v2)) \ r.val.ull = v1.val.ull OP v2.val.sll; \ else \ r.val.ull = v1.val.ull OP v2.val.ull; #define BIN_OP(r, v1, v2, OP) \ switch(v1.type & SIZE_MASK) \ { \ case SIZE_INT: BIN_OP_INT(r, v1, v2, OP); break; \ case SIZE_LONG: BIN_OP_LONG(r, v1, v2, OP); break; \ case SIZE_LONGLONG: BIN_OP_LONGLONG(r, v1, v2, OP); break; \ default: pp_internal_error(__FILE__, __LINE__, "Invalid type indicator (0x%04x)", v1.type); \ } /* * Prototypes */ static int boolean(cval_t *v); static void promote_equal_size(cval_t *v1, cval_t *v2); static void cast_to_sint(cval_t *v); static void cast_to_uint(cval_t *v); static void cast_to_slong(cval_t *v); static void cast_to_ulong(cval_t *v); static void cast_to_sll(cval_t *v); static void cast_to_ull(cval_t *v); static marg_t *new_marg(char *str, def_arg_t type); static marg_t *add_new_marg(char *str, def_arg_t type); static int marg_index(char *id); static mtext_t *new_mtext(char *str, int idx, def_exp_t type); static mtext_t *combine_mtext(mtext_t *tail, mtext_t *mtp); static char *merge_text(char *s1, char *s2); /* * Local variables */ static marg_t **macro_args; /* Macro parameters array while parsing */ static int nmacro_args; %} %union{ int sint; unsigned int uint; long slong; unsigned long ulong; wrc_sll_t sll; wrc_ull_t ull; int *iptr; char *cptr; cval_t cval; marg_t *marg; mtext_t *mtext; } %token tRCINCLUDE %token tIF tIFDEF tIFNDEF tELSE tELIF tENDIF tDEFINED tNL %token tINCLUDE tLINE tGCCLINE tERROR tWARNING tPRAGMA tPPIDENT %token tUNDEF tMACROEND tCONCAT tELIPSIS tSTRINGIZE %token <cptr> tIDENT tLITERAL tMACRO tDEFINE %token <cptr> tDQSTRING tSQSTRING tIQSTRING %token <uint> tUINT %token <sint> tSINT %token <ulong> tULONG %token <slong> tSLONG %token <ull> tULONGLONG %token <sll> tSLONGLONG %token <cptr> tRCINCLUDEPATH %right '?' ':' %left tLOGOR %left tLOGAND %left '|' %left '^' %left '&' %left tEQ tNE %left '<' tLTE '>' tGTE %left tLSHIFT tRSHIFT %left '+' '-' %left '*' '/' %right '~' '!' %type <cval> pp_expr %type <marg> emargs margs %type <mtext> opt_mtexts mtexts mtext %type <sint> allmargs %type <cptr> opt_text text /* ************************************************************************** * The parser starts here ************************************************************************** */ %% pp_file : /* Empty */ | pp_file preprocessor ; preprocessor : tINCLUDE tDQSTRING tNL { pp_do_include($2, 1); } | tINCLUDE tIQSTRING tNL { pp_do_include($2, 0); } | tIF pp_expr tNL { pp_next_if_state(boolean(&$2)); } | tIFDEF tIDENT tNL { pp_next_if_state(pplookup($2) != NULL); free($2); } | tIFNDEF tIDENT tNL { int t = pplookup($2) == NULL; if(pp_incl_state.state == 0 && t && !pp_incl_state.seen_junk) { pp_incl_state.state = 1; pp_incl_state.ppp = $2; pp_incl_state.ifdepth = pp_get_if_depth(); } else if(pp_incl_state.state != 1) { pp_incl_state.state = -1; free($2); } else free($2); pp_next_if_state(t); if(pp_status.debug) fprintf(stderr, "tIFNDEF: %s:%d: include_state=%d, include_ppp='%s', include_ifdepth=%d\n", pp_status.input, pp_status.line_number, pp_incl_state.state, pp_incl_state.ppp, pp_incl_state.ifdepth); } | tELIF pp_expr tNL { pp_if_state_t s = pp_pop_if(); switch(s) { case if_true: case if_elif: pp_push_if(if_elif); break; case if_false: pp_push_if(boolean(&$2) ? if_true : if_false); break; case if_ignore: pp_push_if(if_ignore); break; case if_elsetrue: case if_elsefalse: pperror("#elif cannot follow #else"); default: pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #elif directive", s); } } | tELSE tNL { pp_if_state_t s = pp_pop_if(); switch(s) { case if_true: pp_push_if(if_elsefalse); break; case if_elif: pp_push_if(if_elif); break; case if_false: pp_push_if(if_elsetrue); break; case if_ignore: pp_push_if(if_ignore); break; case if_elsetrue: case if_elsefalse: pperror("#else clause already defined"); default: pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #else directive", s); } } | tENDIF tNL { pp_pop_if(); if(pp_incl_state.ifdepth == pp_get_if_depth() && pp_incl_state.state == 1) { pp_incl_state.state = 2; pp_incl_state.seen_junk = 0; } else if(pp_incl_state.state != 1) { pp_incl_state.state = -1; } if(pp_status.debug) fprintf(stderr, "tENDIF: %s:%d: include_state=%d, include_ppp='%s', include_ifdepth=%d\n", pp_status.input, pp_status.line_number, pp_incl_state.state, pp_incl_state.ppp, pp_incl_state.ifdepth); } | tUNDEF tIDENT tNL { pp_del_define($2); free($2); } | tDEFINE opt_text tNL { pp_add_define($1, $2); } | tMACRO res_arg allmargs tMACROEND opt_mtexts tNL { pp_add_macro($1, macro_args, nmacro_args, $5); } | tLINE tSINT tDQSTRING tNL { fprintf(ppout, "# %d %s\n", $2 , $3); free($3); } | tGCCLINE tSINT tDQSTRING tNL { fprintf(ppout, "# %d %s\n", $2 , $3); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tNL { fprintf(ppout, "# %d %s %d\n", $2, $3, $4); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tSINT tNL { fprintf(ppout, "# %d %s %d %d\n", $2 ,$3, $4, $5); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT tNL { fprintf(ppout, "# %d %s %d %d %d\n", $2 ,$3 ,$4 ,$5, $6); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT tSINT tNL { fprintf(ppout, "# %d %s %d %d %d %d\n", $2 ,$3 ,$4 ,$5, $6, $7); free($3); } | tGCCLINE tNL /* The null-token */ | tERROR opt_text tNL { pperror("#error directive: '%s'", $2); if($2) free($2); } | tWARNING opt_text tNL { ppwarning("#warning directive: '%s'", $2); if($2) free($2); } | tPRAGMA opt_text tNL { fprintf(ppout, "#pragma %s\n", $2 ? $2 : ""); if ($2) free($2); } | tPPIDENT opt_text tNL { if(pp_status.pedantic) ppwarning("#ident ignored (arg: '%s')", $2); if($2) free($2); } | tRCINCLUDE tRCINCLUDEPATH { int nl=strlen($2) +3; char *fn=pp_xmalloc(nl); sprintf(fn,"\"%s\"",$2); free($2); pp_do_include(fn,1); } | tRCINCLUDE tDQSTRING { pp_do_include($2,1); } /*| tNL*/ ; opt_text: /* Empty */ { $$ = NULL; } | text { $$ = $1; } ; text : tLITERAL { $$ = $1; } | tDQSTRING { $$ = $1; } | tSQSTRING { $$ = $1; } | text tLITERAL { $$ = merge_text($1, $2); } | text tDQSTRING { $$ = merge_text($1, $2); } | text tSQSTRING { $$ = merge_text($1, $2); } ; res_arg : /* Empty */ { macro_args = NULL; nmacro_args = 0; } ; allmargs: /* Empty */ { $$ = 0; macro_args = NULL; nmacro_args = 0; } | emargs { $$ = nmacro_args; } ; emargs : margs { $$ = $1; } | margs ',' tELIPSIS { $$ = add_new_marg(NULL, arg_list); nmacro_args *= -1; } ; margs : margs ',' tIDENT { $$ = add_new_marg($3, arg_single); } | tIDENT { $$ = add_new_marg($1, arg_single); } ; opt_mtexts : /* Empty */ { $$ = NULL; } | mtexts { for($$ = $1; $$ && $$->prev; $$ = $$->prev) ; } ; mtexts : mtext { $$ = $1; } | mtexts mtext { $$ = combine_mtext($1, $2); } ; mtext : tLITERAL { $$ = new_mtext($1, 0, exp_text); } | tDQSTRING { $$ = new_mtext($1, 0, exp_text); } | tSQSTRING { $$ = new_mtext($1, 0, exp_text); } | tCONCAT { $$ = new_mtext(NULL, 0, exp_concat); } | tSTRINGIZE tIDENT { int mat = marg_index($2); if(mat < 0) pperror("Stringification identifier must be an argument parameter"); $$ = new_mtext(NULL, mat, exp_stringize); } | tIDENT { int mat = marg_index($1); if(mat >= 0) $$ = new_mtext(NULL, mat, exp_subst); else $$ = new_mtext($1, 0, exp_text); } ; pp_expr : tSINT { $$.type = cv_sint; $$.val.si = $1; } | tUINT { $$.type = cv_uint; $$.val.ui = $1; } | tSLONG { $$.type = cv_slong; $$.val.sl = $1; } | tULONG { $$.type = cv_ulong; $$.val.ul = $1; } | tSLONGLONG { $$.type = cv_sll; $$.val.sl = $1; } | tULONGLONG { $$.type = cv_ull; $$.val.ul = $1; } | tDEFINED tIDENT { $$.type = cv_sint; $$.val.si = pplookup($2) != NULL; } | tDEFINED '(' tIDENT ')' { $$.type = cv_sint; $$.val.si = pplookup($3) != NULL; } | tIDENT { $$.type = cv_sint; $$.val.si = 0; } | pp_expr tLOGOR pp_expr { $$.type = cv_sint; $$.val.si = boolean(&$1) || boolean(&$3); } | pp_expr tLOGAND pp_expr { $$.type = cv_sint; $$.val.si = boolean(&$1) && boolean(&$3); } | pp_expr tEQ pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, ==) } | pp_expr tNE pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, !=) } | pp_expr '<' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <) } | pp_expr '>' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >) } | pp_expr tLTE pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <=) } | pp_expr tGTE pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >=) } | pp_expr '+' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, +) } | pp_expr '-' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, -) } | pp_expr '^' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, ^) } | pp_expr '&' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, &) } | pp_expr '|' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, |) } | pp_expr '*' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, *) } | pp_expr '/' pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, /) } | pp_expr tLSHIFT pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <<) } | pp_expr tRSHIFT pp_expr { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >>) } | '+' pp_expr { $$ = $2; } | '-' pp_expr { UNARY_OP($$, $2, -) } | '~' pp_expr { UNARY_OP($$, $2, ~) } | '!' pp_expr { $$.type = cv_sint; $$.val.si = !boolean(&$2); } | '(' pp_expr ')' { $$ = $2; } | pp_expr '?' pp_expr ':' pp_expr { $$ = boolean(&$1) ? $3 : $5; } ; %% /* ************************************************************************** * Support functions ************************************************************************** */ static void cast_to_sint(cval_t *v) { switch(v->type) { case cv_sint: break; case cv_uint: break; case cv_slong: v->val.si = v->val.sl; break; case cv_ulong: v->val.si = v->val.ul; break; case cv_sll: v->val.si = v->val.sll; break; case cv_ull: v->val.si = v->val.ull; break; } v->type = cv_sint; } static void cast_to_uint(cval_t *v) { switch(v->type) { case cv_sint: break; case cv_uint: break; case cv_slong: v->val.ui = v->val.sl; break; case cv_ulong: v->val.ui = v->val.ul; break; case cv_sll: v->val.ui = v->val.sll; break; case cv_ull: v->val.ui = v->val.ull; break; } v->type = cv_uint; } static void cast_to_slong(cval_t *v) { switch(v->type) { case cv_sint: v->val.sl = v->val.si; break; case cv_uint: v->val.sl = v->val.ui; break; case cv_slong: break; case cv_ulong: break; case cv_sll: v->val.sl = v->val.sll; break; case cv_ull: v->val.sl = v->val.ull; break; } v->type = cv_slong; } static void cast_to_ulong(cval_t *v) { switch(v->type) { case cv_sint: v->val.ul = v->val.si; break; case cv_uint: v->val.ul = v->val.ui; break; case cv_slong: break; case cv_ulong: break; case cv_sll: v->val.ul = v->val.sll; break; case cv_ull: v->val.ul = v->val.ull; break; } v->type = cv_ulong; } static void cast_to_sll(cval_t *v) { switch(v->type) { case cv_sint: v->val.sll = v->val.si; break; case cv_uint: v->val.sll = v->val.ui; break; case cv_slong: v->val.sll = v->val.sl; break; case cv_ulong: v->val.sll = v->val.ul; break; case cv_sll: break; case cv_ull: break; } v->type = cv_sll; } static void cast_to_ull(cval_t *v) { switch(v->type) { case cv_sint: v->val.ull = v->val.si; break; case cv_uint: v->val.ull = v->val.ui; break; case cv_slong: v->val.ull = v->val.sl; break; case cv_ulong: v->val.ull = v->val.ul; break; case cv_sll: break; case cv_ull: break; } v->type = cv_ull; } static void promote_equal_size(cval_t *v1, cval_t *v2) { #define cv_sizeof(v) ((int)(v->type & SIZE_MASK)) int s1 = cv_sizeof(v1); int s2 = cv_sizeof(v2); #undef cv_sizeof if(s1 == s2) return; else if(s1 > s2) { switch(v1->type) { case cv_sint: cast_to_sint(v2); break; case cv_uint: cast_to_uint(v2); break; case cv_slong: cast_to_slong(v2); break; case cv_ulong: cast_to_ulong(v2); break; case cv_sll: cast_to_sll(v2); break; case cv_ull: cast_to_ull(v2); break; } } else { switch(v2->type) { case cv_sint: cast_to_sint(v1); break; case cv_uint: cast_to_uint(v1); break; case cv_slong: cast_to_slong(v1); break; case cv_ulong: cast_to_ulong(v1); break; case cv_sll: cast_to_sll(v1); break; case cv_ull: cast_to_ull(v1); break; } } } static int boolean(cval_t *v) { switch(v->type) { case cv_sint: return v->val.si != (int)0; case cv_uint: return v->val.ui != (unsigned int)0; case cv_slong: return v->val.sl != (long)0; case cv_ulong: return v->val.ul != (unsigned long)0; case cv_sll: return v->val.sll != (wrc_sll_t)0; case cv_ull: return v->val.ull != (wrc_ull_t)0; } return 0; } static marg_t *new_marg(char *str, def_arg_t type) { marg_t *ma = pp_xmalloc(sizeof(marg_t)); ma->arg = str; ma->type = type; ma->nnl = 0; return ma; } static marg_t *add_new_marg(char *str, def_arg_t type) { marg_t *ma = new_marg(str, type); nmacro_args++; macro_args = pp_xrealloc(macro_args, nmacro_args * sizeof(macro_args[0])); macro_args[nmacro_args-1] = ma; return ma; } static int marg_index(char *id) { int t; for(t = 0; t < nmacro_args; t++) { if(!strcmp(id, macro_args[t]->arg)) break; } return t < nmacro_args ? t : -1; } static mtext_t *new_mtext(char *str, int idx, def_exp_t type) { mtext_t *mt = pp_xmalloc(sizeof(mtext_t)); if(str == NULL) mt->subst.argidx = idx; else mt->subst.text = str; mt->type = type; mt->next = mt->prev = NULL; return mt; } static mtext_t *combine_mtext(mtext_t *tail, mtext_t *mtp) { if(!tail) return mtp; if(!mtp) return tail; if(tail->type == exp_text && mtp->type == exp_text) { tail->subst.text = pp_xrealloc(tail->subst.text, strlen(tail->subst.text)+strlen(mtp->subst.text)+1); strcat(tail->subst.text, mtp->subst.text); free(mtp->subst.text); free(mtp); return tail; } if(tail->type == exp_concat && mtp->type == exp_concat) { free(mtp); return tail; } if(tail->type == exp_concat && mtp->type == exp_text) { int len = strlen(mtp->subst.text); while(len) { /* FIXME: should delete space from head of string */ if(isspace(mtp->subst.text[len-1] & 0xff)) mtp->subst.text[--len] = '\0'; else break; } if(!len) { free(mtp->subst.text); free(mtp); return tail; } } if(tail->type == exp_text && mtp->type == exp_concat) { int len = strlen(tail->subst.text); while(len) { if(isspace(tail->subst.text[len-1] & 0xff)) tail->subst.text[--len] = '\0'; else break; } if(!len) { mtp->prev = tail->prev; mtp->next = tail->next; if(tail->prev) tail->prev->next = mtp; free(tail->subst.text); free(tail); return mtp; } } tail->next = mtp; mtp->prev = tail; return mtp; } static char *merge_text(char *s1, char *s2) { int l1 = strlen(s1); int l2 = strlen(s2); s1 = pp_xrealloc(s1, l1+l2+1); memcpy(s1+l1, s2, l2+1); free(s2); return s1; }