Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}