| @@ -83,18 +83,76 @@ | |||||
| else FAIL_ON_COND_(true, ED_PP_UNKNOWN) | else FAIL_ON_COND_(true, ED_PP_UNKNOWN) | ||||
| /* | /* | ||||
| Preprocess a single source line (source, length) into a normalized ASMLine. | |||||
| Return whether the given character is a valid label character. | |||||
| */ | |||||
| static inline bool is_valid_label_char(char c, bool first) | |||||
| { | |||||
| return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || | |||||
| (!first && c >= '0' && c <= '9') || c == '_'; | |||||
| } | |||||
| /* | |||||
| Preprocess a single source line for labels. | |||||
| Return the index of first non-whitespace non-label character. *head_ptr is | |||||
| updated to the first label in sequence, and *tail_ptr to the last. Both | |||||
| will be set to NULL if the line doesn't contain labels. | |||||
| */ | |||||
| static size_t read_labels( | |||||
| const char *source, size_t length, ASMLine **head_ptr, ASMLine **tail_ptr) | |||||
| { | |||||
| size_t start = 0, i, nexti; | |||||
| while (start < length && (source[start] == ' ' || source[start] == '\t')) | |||||
| start++; | |||||
| i = start; | |||||
| while (i < length && is_valid_label_char(source[i], i == start)) | |||||
| i++; | |||||
| if (i == start || i == length || source[i] != ':') { | |||||
| *head_ptr = NULL; | |||||
| *tail_ptr = NULL; | |||||
| return 0; | |||||
| } | |||||
| ASMLine *line = malloc(sizeof(ASMLine)); | |||||
| if (!line) | |||||
| OUT_OF_MEMORY() | |||||
| line->data = malloc(sizeof(char) * (i - start + 1)); | |||||
| if (!line->data) | |||||
| OUT_OF_MEMORY() | |||||
| *Only* the data and length fields in the ASMLine object are populated. The | |||||
| normalization process converts tabs to spaces, lowercases all alphabetical | |||||
| characters, and removes runs of multiple spaces (outside of string | |||||
| literals), strips comments, and other things. | |||||
| strncpy(line->data, source + start, i - start + 1); | |||||
| line->length = i - start + 1; | |||||
| nexti = read_labels(source + i + 1, length - i - 1, &line->next, tail_ptr); | |||||
| *head_ptr = line; | |||||
| if (!nexti) | |||||
| *tail_ptr = line; | |||||
| return i + 1 + nexti; | |||||
| } | |||||
| /* | |||||
| Preprocess a single source line (source, length) into one or more ASMLines. | |||||
| Only the data, length, and next fields of the ASMLine objects are | |||||
| populated. The normalization process strips comments, makes various | |||||
| adjustments outside of string literals (converts tabs to spaces, lowercases | |||||
| all alphabetical characters, and removes runs of multiple spaces), among | |||||
| other things. | |||||
| Return NULL if an ASM line was not generated from the source, i.e. if it is | Return NULL if an ASM line was not generated from the source, i.e. if it is | ||||
| blank after being stripped. | blank after being stripped. | ||||
| */ | */ | ||||
| static ASMLine* normalize_line(const char *source, size_t length) | static ASMLine* normalize_line(const char *source, size_t length) | ||||
| { | { | ||||
| ASMLine *head, *tail; | |||||
| size_t offset = read_labels(source, length, &head, &tail); | |||||
| source += offset; | |||||
| length -= offset; | |||||
| char *data = malloc(sizeof(char) * length); | char *data = malloc(sizeof(char) * length); | ||||
| if (!data) | if (!data) | ||||
| OUT_OF_MEMORY() | OUT_OF_MEMORY() | ||||
| @@ -138,7 +196,7 @@ static ASMLine* normalize_line(const char *source, size_t length) | |||||
| if (!has_content) { | if (!has_content) { | ||||
| free(data); | free(data); | ||||
| return NULL; | |||||
| return head; | |||||
| } | } | ||||
| ASMLine *line = malloc(sizeof(ASMLine)); | ASMLine *line = malloc(sizeof(ASMLine)); | ||||
| @@ -151,6 +209,12 @@ static ASMLine* normalize_line(const char *source, size_t length) | |||||
| line->data = data; | line->data = data; | ||||
| line->length = di; | line->length = di; | ||||
| line->next = NULL; | |||||
| if (head) { // Line has labels, so link the main part up | |||||
| tail->next = line; | |||||
| return head; | |||||
| } | |||||
| return line; | return line; | ||||
| } | } | ||||
| @@ -240,19 +304,28 @@ static ErrorInfo* build_asm_lines( | |||||
| { | { | ||||
| ErrorInfo *ei; | ErrorInfo *ei; | ||||
| ASMLine dummy = {.next = NULL}; | ASMLine dummy = {.next = NULL}; | ||||
| ASMLine *line, *prev = &dummy; | |||||
| ASMLine *line, *prev = &dummy, *temp; | |||||
| const Line *orig, *next_orig = source->lines; | const Line *orig, *next_orig = source->lines; | ||||
| while ((orig = next_orig)) { | while ((orig = next_orig)) { | ||||
| line = normalize_line(orig->data, orig->length); | |||||
| line = temp = normalize_line(orig->data, orig->length); | |||||
| next_orig = orig->next; | next_orig = orig->next; | ||||
| if (!line) | if (!line) | ||||
| continue; | continue; | ||||
| // Populate ASMLine fields not set by normalize_line(): | // Populate ASMLine fields not set by normalize_line(): | ||||
| line->original = orig; | |||||
| line->filename = source->filename; | |||||
| line->next = NULL; | |||||
| while (temp) { | |||||
| temp->original = orig; | |||||
| temp->filename = source->filename; | |||||
| temp = temp->next; | |||||
| } | |||||
| // If there are multiple ASMLines, all but the last must be labels: | |||||
| while (line->next) { | |||||
| prev->next = line; | |||||
| prev = line; | |||||
| line = line->next; | |||||
| } | |||||
| if (IS_DIRECTIVE(line, DIR_INCLUDE)) { | if (IS_DIRECTIVE(line, DIR_INCLUDE)) { | ||||
| char *path = read_include_path(line); | char *path = read_include_path(line); | ||||