static void handle_define(TCC_CONTEXT *tc, sym_t mid, CMacro *ma) { enum { STA_OKAY, STA_LEFT_PARENTHESIS, STA_PARAMETER, STA_SEPERATOR, STA_TRIDOT, } state; const char *line = ma->line; CToken token; if( ! get_token(tc, &line, &token, false) ) return; if(token.attr == CToken::TA_IDENT) { if(*line == '(') { CC_ARRAY<sym_t> para_list; XCHAR *xc; const char *last_pos; sym_t last_para = SSID_INVALID; bool has_va_args = false; state = STA_LEFT_PARENTHESIS; line++; while( get_token(tc, &line, &token, false) ) { switch(state) { case STA_LEFT_PARENTHESIS: if( token.id == SSID_RIGHT_PARENTHESIS) { state = STA_OKAY; goto okay; } else if(token.attr == CToken::TA_IDENT) { para_list.push_back(token.id); state = STA_SEPERATOR; } else if(token.id == SSID_TRIDOT) { state = STA_TRIDOT; continue; } else { gEx.format("\"%s\" may not appear in macro parameter list", TR(tc,token.id)); goto error; } break; case STA_SEPERATOR: if(token.id == SSID_RIGHT_PARENTHESIS) { state = STA_OKAY; goto okay; } else if(token.id == SSID_COMMA) state = STA_PARAMETER; else if(token.id == SSID_TRIDOT) { state = STA_TRIDOT; } else { gEx = "macro parameters must be comma-separated"; goto error; } break; case STA_PARAMETER: if(token.id == SSID_TRIDOT) { state = STA_TRIDOT; continue; } else if(token.attr == CToken::TA_IDENT) { para_list.push_back(token.id); state = STA_SEPERATOR; } else { gEx = "parameter name missing"; goto error; } break; case STA_TRIDOT: if(token.id != SSID_RIGHT_PARENTHESIS) { gEx = "missing ')' in macro parameter list"; throw &gEx; } has_va_args = true; if(last_para == SSID_COMMA || last_para == SSID_LEFT_PARENTHESIS) { para_list.push_back(SSID_VA_ARGS); last_para = SSID_VA_ARGS; } goto okay; default: assert(0); } last_para = token.id; } if(state != STA_OKAY) { gEx = "missing ')' in macro parameter list"; throw &gEx; } okay: xc = (XCHAR*) malloc(sizeof(XCHAR) * strlen(line) + 4); ma->id = mid; ma->parsed = xc; ma->nr_args = para_list.size(); ma->va_args = has_va_args; skip_blanks(line); last_pos = line; sym_t prev_sid = SSID_INVALID; while( ReadToken(tc, &line, &token, &gEx, false) ) { if(gEx.GetError() != NULL) throw (&gEx); if(token.id == SSID_DUAL_SHARP) { if(xc == ma->parsed) { gEx = "'##' cannot appear at either end of a macro expansion"; goto error; } else if( IS_MA_PAR0(*(xc-1)) ) *(xc-1) |= XF_MA_PAR2; } if(prev_sid == SSID_DUAL_SHARP) { XCHAR *yc = xc - 3; while( yc >= ma->parsed && (*yc == '\t' || *yc == ' ') ) yc--; xc = yc + 1; assert(*xc == '#' || *xc == ' ' || *xc == '\t'); } if( token.attr == CToken::TA_IDENT ) { int magic = 0; int16_t para_ord; static const XCHAR xflags[3] = { XF_MA_PAR0, XF_MA_PAR1, XF_MA_PAR2 }; if( has_va_args && (token.id == last_para || token.id == SSID_VA_ARGS) ) para_ord = ma->nr_args - 1; else para_ord = find(token.id, para_list); if(prev_sid == SSID_SHARP) { if(para_ord < 0) { gEx = "'#' is not followed by a macro parameter"; goto error; } magic = 1; xc--; } else if(prev_sid == SSID_DUAL_SHARP) { magic = 2; } if(para_ord < 0) goto do_cat; *xc++ = para_ord | xflags[magic]; } else { do_cat: if(prev_sid == SSID_DUAL_SHARP) { skip_blanks(last_pos); } join(&xc, last_pos, line); } last_pos = line; prev_sid = token.id; } /*end while*/ *xc = 0; } else { XCHAR *xc; xc = (XCHAR*) malloc(sizeof(XCHAR) * strlen(line) + 4); ma->id = mid; ma->parsed = xc; ma->nr_args = CMacro::OL_M; skip_blanks(line); while(1) { char c = *line; if( iseol(c) ) { *xc = '\0'; break; } *xc = c; xc++, line++; } } } // dump(ma->parsed); return; error: throw &gEx; }
CC_STRING CMaExpander::Expand_FLM(CMacro *ma) { CC_STRING outs; CC_ARRAY<CC_STRING> margs; CC_STRING carg; const char *p = pos; int level; skip_blanks(p); if( iseol(*p) ) { gex.format("Macro \"%s\" expects arguments", TR(tc,ma->id)); throw &gex; } if( *p != '(' ) { gex = "Macro expects '('"; throw &gex; } p++; skip_blanks(p); level = 1; while( 1 ) { if( iseol(*p) ) break; if( *p == ',' ) { if( level == 1 && ( ! ma->va_args || (margs.size() + 1 < ma->nr_args) ) ) { Trim(carg); margs.push_back(carg); carg.clear(); } else { carg += ','; skip_blanks(p); } } else if(*p == '(') { level ++; carg += '('; } else if(*p == ')') { level --; if(level == 0) { p++; Trim(carg); margs.push_back(carg); carg.clear(); break; } else carg += ')'; } else { carg += *p; } p++; } pos = p; XCHAR *xc; CC_STRING s; size_t n; n = ma->nr_args; assert(n != CMacro::OL_M); if(n != margs.size()) { if( ma->va_args && margs.size() + 1 == n ) { margs.push_back(CC_STRING("")); } else { gex.format("Macro \"%s\" requires %u arguments, but %u given", TR(tc,ma->id), n, margs.size() ); throw &gex; } } for(xc = ma->parsed; *xc != 0; xc++) { if(IS_MA_PAR2(*xc) || IS_MA_PAR0(*xc)) { const CC_STRING& carg = margs[(uint8_t)*xc]; CMaExpander expander2(tc, carg.c_str(), for_include); CC_STRING tmp; tmp = expander2.TryExpand(); s += tmp; } else if(IS_MA_PAR1(*xc)) { const CC_STRING& carg = margs[(uint8_t)*xc]; s += '"'; s += carg; s += '"'; } else { s += (char) *xc; } } outs += s; return outs; }