/* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 (signed) v2.val.ui; \ else if(!cv_signed(v1) && cv_signed(v2)) \ r.val.si = (signed) 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 (signed long) v2.val.ul; \ else if(!cv_signed(v1) && cv_signed(v2)) \ r.val.sl = (signed long) 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 (wrc_sll_t) v2.val.ull; \ else if(!cv_signed(v1) && cv_signed(v2)) \ r.val.sll = (wrc_sll_t) 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: ppy_error("#elif cannot follow #else"); break; case if_error: break; 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: ppy_error("#else clause already defined"); break; case if_error: break; default: pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #else directive", s); } } | tENDIF tNL { if(pp_pop_if() != if_error) { 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); free($1); free($2); } | tMACRO res_arg allmargs tMACROEND opt_mtexts tNL { pp_add_macro($1, macro_args, nmacro_args, $5); } | tLINE tSINT tDQSTRING tNL { if($3) pp_writestring("# %d %s\n", $2 , $3); free($3); } | tGCCLINE tSINT tDQSTRING tNL { if($3) pp_writestring("# %d %s\n", $2 , $3); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tNL { if($3) pp_writestring("# %d %s %d\n", $2, $3, $4); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tSINT tNL { if($3) pp_writestring("# %d %s %d %d\n", $2 ,$3, $4, $5); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT tNL { if($3) pp_writestring("# %d %s %d %d %d\n", $2 ,$3 ,$4 ,$5, $6); free($3); } | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT tSINT tNL { if($3) pp_writestring("# %d %s %d %d %d %d\n", $2 ,$3 ,$4 ,$5, $6, $7); free($3); } | tGCCLINE tNL /* The null-token */ | tERROR opt_text tNL { ppy_error("#error directive: '%s'", $2); free($2); } | tWARNING opt_text tNL { ppy_warning("#warning directive: '%s'", $2); free($2); } | tPRAGMA opt_text tNL { pp_writestring("#pragma %s\n", $2 ? $2 : ""); free($2); } | tPPIDENT opt_text tNL { if(pp_status.pedantic) ppy_warning("#ident ignored (arg: '%s')", $2); free($2); } | tRCINCLUDE tRCINCLUDEPATH { if($2) { int nl=strlen($2) +3; char *fn=pp_xmalloc(nl); if(fn) { sprintf(fn,"\"%s\"",$2); pp_do_include(fn,1); } free($2); } } | 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) ppy_error("Stringification identifier must be an argument parameter"); else $$ = new_mtext(NULL, mat, exp_stringize); } | tIDENT { int mat = marg_index($1); if(mat >= 0) $$ = new_mtext(NULL, mat, exp_subst); else if($1) $$ = 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.sll = $1; } | tULONGLONG { $$.type = cv_ull; $$.val.ull = $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)); if(!ma) return NULL; 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 **new_macro_args; marg_t *ma; if(!str) return NULL; new_macro_args = pp_xrealloc(macro_args, (nmacro_args+1) * sizeof(macro_args[0])); if(!new_macro_args) return NULL; macro_args = new_macro_args; ma = new_marg(str, type); if(!ma) return NULL; macro_args[nmacro_args] = ma; nmacro_args++; return ma; } static int marg_index(char *id) { int t; if(!id) return -1; 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(!mt) return NULL; 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) { char *new_text; new_text = pp_xrealloc(tail->subst.text, strlen(tail->subst.text)+strlen(mtp->subst.text)+1); if(!new_text) return mtp; tail->subst.text = new_text; 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; int l2; char *snew; if(!s1) return s2; if(!s2) return s1; l1 = strlen(s1); l2 = strlen(s2); snew = pp_xrealloc(s1, l1+l2+1); if(!snew) { free(s2); return s1; } s1 = snew; memcpy(s1+l1, s2, l2+1); free(s2); return s1; }