expression* make_expression(program_state* prog) { expr_block* b = (expr_block*)cvec_back_void(&prog->expressions); if (b->n == b->used) { expr_block b_new; make_expression_block(32, &b_new); cvec_push_void(&prog->expressions, &b_new); b = (expr_block*)cvec_back_void(&prog->expressions); } return &b->data[b->used++]; }
void handle_define(preprocessor_state* preproc) { FILE* input = preproc->input; lexer_state* lexer = &preproc->lexer; cvector_token_lex tlex; cvec_token_lex(&tlex, 0, 10, free_token_lex, NULL); token_lex tok_lex[4]; tok_lex[0] = read_token(input, lexer, NULL); if (tok_lex[0].tok.type != ID) { preprocessor_error(&tok_lex[0], lexer, "expected ID for macro name,"); } macro_params p = { -1, NULL }; char* check_val = look_up_macro(preproc, tok_lex[0].tok.v.id); lexer_state save_lex = *lexer; long fpos = ftell(input); tok_lex[1] = read_token(input, lexer, NULL); if (tok_lex[1].line != tok_lex[0].line) { //object macro empty string *lexer = save_lex; if (fseek(input, fpos, SEEK_SET)) { perror("fseek failure in handle_define"); exit(0); } if (check_val && strcmp(check_val, "")) { preprocessor_error(NULL, lexer, "redefinition of %s\n", tok_lex[0].tok.v.id); } if (!check_val) { cvec_push_str(&preproc->macros, tok_lex[0].tok.v.id); cvec_push_str(&preproc->values, ""); cvec_push_void(&preproc->params, &p); } goto exit; } if (tok_lex[1].tok.type == LPAREN) { //function style macro int n = 0, i; tok_lex[2] = read_token(input, lexer, NULL); while (tok_lex[2].tok.type == ID) { n++; cvec_push_token_lex(&tlex, &tok_lex[2]); //don't free id because vector will tok_lex[3] = read_token(input, lexer, NULL); if (tok_lex[3].tok.type != COMMA) break; tok_lex[2] = read_token(input, lexer, NULL); } //TODO better way to express error logic? if (tok_lex[2].tok.type != ID) { if (n != 0 || n == 0 && tok_lex[2].tok.type != RPAREN) preprocessor_error(NULL, lexer, "improperly formed function style macro\n"); } else if (tok_lex[3].tok.type != RPAREN) { preprocessor_error(NULL, lexer, "improperly formed function style macro\n"); } p.num_params = n; if (n) { p.names = malloc(n * sizeof(char*)); assert(p.names); } for (i=0; i < n; ++i) { p.names[i] = mystrdup(tlex.a[i].tok.v.id); } } else { //put it back, it's part of the macro value //it's handled below *lexer = save_lex; if (fseek(input, fpos, SEEK_SET)) { perror("fseek failure in preprocess_file"); exit(0); } } memset(macro_buf, 0, MAX_MACRO_LEN); save_lex = *lexer; if ((fpos = ftell(input)) == -1) { perror("ftell failure in handle_define"); exit(0); } tok_lex[2] = read_token(input, lexer, NULL); while (tok_lex[0].line == lexer->cur_line) { cvec_push_token_lex(&tlex, &tok_lex[2]); save_lex = *lexer; if ((fpos = ftell(input)) == -1) { perror("ftell failure in handle_define"); exit(0); } //don't free id bceause vector will do it tok_lex[2] = read_token(input, lexer, NULL); } if (tok_lex[2].tok.type == ID || tok_lex[2].tok.type == STR_LITERAL) free(tok_lex[2].tok.v.id); *lexer = save_lex; if (fseek(input, fpos, SEEK_SET)) { perror("fseek failue in handle_define"); exit(0); } int buf_pos = 0; int res, len; for (int i= (p.num_params == -1) ? 0 : p.num_params; i < tlex.size; ++i) { res = print_token_to_str(&tlex.a[i].tok, ¯o_buf[buf_pos], MAX_MACRO_LEN-buf_pos); if (res < 0 || res >= MAX_MACRO_LEN - buf_pos) { preprocessor_error(NULL, lexer, "macro is too long or output error\n"); } len = res; buf_pos += res; // inserting a space for each whitespace character // ie it wouldn't look the same if the programmer used any tabs etc but it's good enough if (i != tlex.size-1 && tlex.a[i+1].pos > tlex.a[i].pos + len) { res = snprintf(¯o_buf[buf_pos], MAX_MACRO_LEN-buf_pos, "%*s", tlex.a[i+1].pos - (tlex.a[i].pos + len), ""); if (res < 0 || res >= MAX_MACRO_LEN - buf_pos) { preprocessor_error(NULL, lexer, "macro is too long or output error\n"); } buf_pos += res; } } //benign redefinitions are allowed (ie the definitions are the same) if (check_val && strcmp(check_val, macro_buf)) { preprocessor_error(NULL, lexer, "redefinition of macro %s\n", tlex.a[0].tok.v.id); } //push new macro cvec_push_str(&preproc->macros, tok_lex[0].tok.v.id); cvec_push_str(&preproc->values, macro_buf); cvec_push_void(&preproc->params, &p); exit: free(tok_lex[0].tok.v.id); // name of macro if (tok_lex[1].tok.type == ID || tok_lex[1].tok.type == STR_LITERAL) free(tok_lex[1].tok.v.id); cvec_free_token_lex(&tlex); }