static void skip_to_endln(state_t* state) { char c; while (ppimpl_has_input(state)) { c = ppimpl_get_input(state); if (c == '\n') return; } }
/// /// Performs preprocessing using the internally /// constructed state object. /// void ppimpl_process(state_t* state) { char c; match_t* m; bool reprocess = false; size_t i; scope_t* current; while (ppimpl_has_input(state)) { // Fetch a character from the input. c = ppimpl_get_input(state); // Push it onto the output. list_append(&state->cached_output, (void*)c); // Update string tracking states. ppimpl_track_strings(state); // If we are in a single or double string, no matching // operations can be performed. if (state->in_single_string || state->in_double_string) continue; // We keep process once unless we're asked to restart // by a match. This is used when a match changes the // handlers (such as appending a new handler or removing // an old one), and we need to restart the matching process. do { reprocess = false; // Iterate through all of our match handlers. for (i = 0; i < list_size(&state->handlers); i++) { m = (match_t*)list_get_at(&state->handlers, i); // Test for a match and if so, handle it. if (ppimpl_match_test(state, m)) { // Remove the characters from the cached input // depending on the match text length. list_delete_range(&state->cached_output, list_size(&state->cached_output) - blength(m->text.ref), list_size(&state->cached_output) - 1); // Reset the printing index. state->print_index = 0; // Call the match handler. m->handler(state, m, &reprocess); if (reprocess) break; } } } while (reprocess); } // We have finished processing; everything is stored in the cached output, // so now we push everything that was in the cached output. while (list_size(&state->cached_output) > 0) state->output((char)list_extract_at(&state->cached_output, 0)); }
/// /// Checks to see if the current output cache /// has a match against a specified match definition. /// bool ppimpl_match_test(state_t* state, match_t* match) { size_t i; int32_t j; // Check if the output cache is even long enough to hold // the match. if (list_size(&state->cached_output) < (size_t)blength(match->text.ref)) return false; // Check each character until we find one that doesn't // match, going backwards through the list (remember that // we're matching against the end of the list). for (i = 0; i < (size_t)blength(match->text.ref); i++) { if (!match->case_insensitive) { if ((char)list_get_at(&state->cached_output, list_size(&state->cached_output) - i - 1) != (char)match->text.ref->data[blength(match->text.ref) - i - 1]) return false; } else { if (tolower((char)list_get_at(&state->cached_output, list_size(&state->cached_output) - i - 1)) != tolower((char)match->text.ref->data[blength(match->text.ref) - i - 1])) return false; } } // We have a match, ensure that it is at the start of the line // if required. if (match->line_start_only) { // We have to make sure that we only have spaces or tabs between // the first character of the text and a \n or the start of the output. j = (int32_t)(list_size(&state->cached_output) - (size_t)blength(match->text.ref) - 1); while (j >= 0) { switch ((char)list_get_at(&state->cached_output, j)) { case ' ': case '\t': // Permitted; continue. break; case '\n': // This means that it is at the start of a line. return true; default: // Anything else means that it's not at the start. return false; } j--; } // If we reach here, check if i == 0, which means that we've // matched the start of the input (which also counts as a match). if (j <= 0) return true; else assert(false); // Something is not right. } // We have a match, ensure it is a proper identifier. if (match->identifier_only) { // We have to get the surrounding characters. bool has_next = ppimpl_has_input(state); bool has_previous = (list_size(&state->cached_output) >= (size_t)blength(match->text.ref) + 1); char next; char previous; bool is_identifier = false; if (has_next) { next = ppimpl_get_input(state); list_insert_at(&state->cached_input, (void*)next, 0); } if (has_previous) previous = (char)list_get_at(&state->cached_output, list_size(&state->cached_output) - blength(match->text.ref) - 1); // Check to see if the preceeding character (before the // match) and the proceeding character (after the match) // both isolate this match as an identifier. if (has_next && has_previous) is_identifier = ppimpl_isolates(next, false) && ppimpl_isolates(previous, true); else if (has_next) is_identifier = ppimpl_isolates(next, false); else if (has_previous) is_identifier = ppimpl_isolates(previous, true); else is_identifier = true; return is_identifier; } // We have a match that can be matched anywhere. return true; }
static void macro_handle(state_t* state, match_t* match, bool* reprocess) { bstring name; bstring temp; list_t parameters; list_t arguments; bool getting_name = true; bool getting_parameters = false; bool getting_arguments = false; struct replace_info* info = match->userdata; int i = 0; int argument_brackets = 0; char c; char* start_loc; match_t* new_match; struct replace_info* new_info; // Parse the parameters out of the name. list_init(¶meters); temp = bfromcstr(""); for (i = 0; i < blength(info->full); i++) { c = info->full->data[i]; if (getting_name) { if (c == '(') { getting_name = false; getting_parameters = true; name = bstrcpy(temp); bassigncstr(temp, ""); } else bconchar(temp, c); } else if (getting_parameters) { if (c == ',' || c == ')') { btrimws(temp); list_append(¶meters, bstrcpy(temp)); bassigncstr(temp, ""); if (c == ')') { getting_parameters = false; break; } } else bconchar(temp, c); } } // Attempt to accept an open bracket. c = ppimpl_get_input(state); while (c == '\1') { // Consume macro termination. i = 0; while (i < strlen("\1MACROTERMINATE\1")) { if (c != "\1MACROTERMINATE\1"[i++]) dhalt(ERR_PP_EXPECTED_OPEN_BRACKET, ppimpl_get_location(state)); c = ppimpl_get_input(state); } ppimpl_pop_scope(state); } if (c != '(') dhalt(ERR_PP_EXPECTED_OPEN_BRACKET, ppimpl_get_location(state)); // Read arguments. getting_arguments = true; list_init(&arguments); start_loc = ppimpl_get_location(state); bassigncstr(temp, ""); while (ppimpl_has_input(state) && getting_arguments) { c = ppimpl_get_input(state); if (c == '(') { argument_brackets++; bconchar(temp, c); } else if (c == ')' && argument_brackets != 0) { argument_brackets--; bconchar(temp, c); } else if (c == ')' && argument_brackets == 0) { list_append(&arguments, bstrcpy(temp)); bassigncstr(temp, ""); getting_arguments = false; break; } else if (c == ',' && argument_brackets == 0) { list_append(&arguments, bstrcpy(temp)); bassigncstr(temp, ""); } else bconchar(temp, c); } if (getting_arguments) dhalt(ERR_PP_NO_TERMINATING_BRACKET, start_loc); // Check to see if the argument count is correct. if (list_size(&arguments) > list_size(¶meters)) dhalt(ERR_PP_TOO_MANY_PARAMETERS, start_loc); else if (list_size(&arguments) < list_size(¶meters)) dhalt(ERR_PP_NOT_ENOUGH_PARAMETERS, start_loc); free(start_loc); // Create a new scope for macro evaluation. ppimpl_push_scope(state, true); // Define the new handlers. for (i = 0; i < list_size(¶meters); i++) { new_info = malloc(sizeof(struct replace_info)); new_info->full = list_get_at(¶meters, i); new_info->replacement = list_get_at(&arguments, i); if (biseq(new_info->full, new_info->replacement)) { free(new_info); continue; } new_match = malloc(sizeof(match_t)); new_match->text = bautofree(list_get_at(¶meters, i)); new_match->handler = replace_handle; new_match->line_start_only = false; new_match->identifier_only = true; new_match->userdata = new_info; new_match->case_insensitive = false; ppimpl_register(state, new_match); } // Print out the macro evaluation and terminator. ppimpl_printf(state, "%s\1MACROTERMINATE\1", info->replacement->data); }
static void define_handle(state_t* state, match_t* match, bool* reprocess) { // We need to parse this manually because we're interested in getting // the first word and then all of the content until a line that doesn't end // with "\". bstring name = bfromcstr(""); bstring word = bfromcstr(""); bstring definition = bfromcstr(""); bool getting_word = true; bool getting_definition = true; bool is_macro = false; match_t* new_match; struct replace_info* info; // Get the first word. while (getting_word) { char c = ppimpl_get_input(state); bconchar(word, c); if (!is_macro && c != '(') bconchar(name, c); bltrimws(word); // Handle termination. if (blength(word) > 0 && (c == ' ' || c == '\t') && !is_macro) { // End of word. btrimws(word); btrimws(name); getting_word = false; } else if (blength(word) > 0 && c == '(' && !is_macro) { // Start of macro. is_macro = true; } else if (blength(word) > 0 && c == '(' && is_macro) { // Second ( in a macro; error. dhalt(ERR_PP_MACRO_MALFORMED, ppimpl_get_location(state)); } else if (blength(word) > 0 && c == ')' && is_macro) { // End of macro name. btrimws(word); btrimws(name); getting_word = false; } else if (blength(word) == 0 && c == '\n') dhalt(ERR_PP_C_DEFINE_PARAMETERS_INCORRECT, ppimpl_get_location(state)); else if (blength(word) > 0 && c == '\n') { // End of word. btrimws(word); btrimws(name); getting_word = false; getting_definition = false; ppimpl_printf(state, "\n"); } } // Get the definition. while (getting_definition) { char c = ppimpl_get_input(state); bconchar(definition, c); bltrimws(definition); if (c == '\n') { if (blength(definition) > 1 && definition->data[blength(definition) - 2] == '\\') { // Remove the new slash. bdelete(definition, blength(definition) - 2, 1); ppimpl_oprintf(state, "\n"); } else { btrimws(definition); getting_definition = false; ppimpl_printf(state, "\n"); } } else if (c == '/' || c == '*') { if (blength(definition) > 1 && definition->data[blength(definition) - 2] == '/') { // a line or block comment ppimpl_iprintf(state, "/%c", c); // remove the slashes bdelete(definition, blength(definition) - 2, 2); btrimws(definition); getting_definition = false; } } } if (blength(definition) == 0 && !is_macro) bassigncstr(definition, "1"); // Create the new replacement handler. info = malloc(sizeof(struct replace_info)); info->full = word; info->replacement = definition; if (biseq(info->full, info->replacement)) { free(info); return; } new_match = malloc(sizeof(match_t)); new_match->text = bautofree(name); if (is_macro) new_match->handler = macro_handle; else new_match->handler = replace_handle; new_match->line_start_only = false; new_match->identifier_only = true; new_match->userdata = info; new_match->case_insensitive = false; ppimpl_register(state, new_match); *reprocess = true; }
static bstring skip_to_endif(state_t* state, bool stop_at_else, bool* stopped_at_else) { char c; bool fresh_line = true; bstring temp = bfromcstr(""); bstring temp_output = bfromcstr(""); bstring output = bfromcstr(""); int if_open = 1; while (ppimpl_has_input(state)) { c = ppimpl_get_input(state); switch(c) { case '#': case '.': if (!fresh_line) { bconchar(output, c); break; } bassigncstr(temp_output, "#"); // first skip spaces while (ppimpl_has_input(state)) { c = ppimpl_get_input(state); bconchar(temp_output, c); if (c != ' ' && c != '\t') break; } // read pp directive bassigncstr(temp, ""); bconchar(temp, c); while (ppimpl_has_input(state)) { c = ppimpl_get_input(state); bconchar(temp_output, c); if (c == ' ' || c == '\t' || c == '\n') break; bconchar(temp, c); } btolower(temp); if (biseq(temp, bfromcstr("endif"))) { if_open--; if (if_open == 0) { if (c != '\n') skip_to_endln(state); *stopped_at_else = false; return output; } } else if (biseq(temp, bfromcstr("if"))) { if_open++; } else if (biseq(temp, bfromcstr("else")) && stop_at_else) { if (if_open == 1) { if (c != '\n') skip_to_endln(state); *stopped_at_else = true; return output; } } bconcat(output, temp_output); fresh_line = (c == '\n'); break; case '\n': fresh_line = true; bconchar(output, c); break; case ' ': case '\t': bconchar(output, c); break; default: fresh_line = false; bconchar(output, c); break; } } // No .ENDIF was found. dhalt(ERR_PP_ASM_NO_ENDIF_TO_IF, ppimpl_get_location(state)); // dhalt will trigger before this, but the compiler warns about // control potentially reaching the end of this function. return NULL; }