diff --git a/Makefile b/Makefile index 1f10c59..712c07e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ OS = linux -VERSION = 1.4.2 +VERSION = 1.4.2b DISTFILES = src \ includes \ @@ -16,6 +16,7 @@ TARGET_INCLUDE_PATH ?= $(PREFIX)/include/avr CDEFS = -DDEFAULT_INCLUDE_PATH='"$(TARGET_INCLUDE_PATH)"' \ -DVERSION='"$(VERSION)"' +CDEFS += $(USER_DEFS) export CDEFS .PHONY: all diff --git a/src/avra.c b/src/avra.c index 5aec61d..5af6bce 100644 --- a/src/avra.c +++ b/src/avra.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "misc.h" @@ -35,8 +36,6 @@ #include "avra.h" #include "device.h" -#define debug 0 - const char *title = "AVRA: advanced AVR macro assembler (version %s)\n"; const char *usage = @@ -90,7 +89,7 @@ main(int argc, const char *argv[]) #if debug == 1 int i; for (i = 0; i < argc; i++) { - printf(argv[i]); + printf("%s", argv[i]); printf("\n"); } #endif @@ -259,42 +258,27 @@ assemble(struct prog_info *pi) int load_arg_defines(struct prog_info *pi) { - int i; - char *expr; - char buff[256]; struct data_list *define; + char *value; + char buff[256]; for (define = GET_ARG_LIST(pi->args, ARG_DEFINE); define; define = define->next) { +#if debug == 1 + printf("load_arg_defines opt \"%s\"\n", define->data); +#endif strcpy(buff, define->data); - expr = get_next_token(buff, TERM_EQUAL); - if (expr) { - /* we reach this, when there is actually a value passed.. */ - if (!get_expr(pi, expr, &i)) { - return (False); - } - } else { - /* if user didnt specify a value, we default to 1 */ - i = 1; - } - /* Forward references allowed. But check, if everything is ok... */ - if (pi->pass==PASS_1) { /* Pass 1 */ - if (test_constant(pi,buff,NULL)!=NULL) { - fprintf(stderr,"Error: Can't define symbol %s twice\n", buff); - return (False); - } - if (def_const(pi, buff, i)==False) - return (False); - } else { /* Pass 2 */ - int j; - if (get_constant(pi, buff, &j)==False) { /* Defined in Pass 1 and now missing ? */ - fprintf(stderr,"Constant %s is missing in pass 2\n",buff); + value = get_next_token(buff, TERM_EQUAL); + if (!value) + value = "1"; + if (pi->pass == PASS_1) { + if (test_preproc_macro(pi,buff,NULL)!=NULL) { + fprintf(stderr,"Error: Can't define preprocessor macro %s twice\n", buff); return (False); } - if (i != j) { - fprintf(stderr,"Constant %s changed value from %d in pass1 to %d in pass 2\n",buff,j,i); + if (def_preproc_macro(pi, buff, PREPROC_MACRO_OBJECT_LIKE, NULL, value)==False) return (False); - } - /* OK. Definition is unchanged */ + } else { + /* Pass 2 */ } } return (True); @@ -396,6 +380,7 @@ free_pi(struct prog_info *pi) free_ifdef_blacklist(pi); free_ifndef_blacklist(pi); free_orglist(pi); + free_preproc_macros(pi); } void @@ -871,6 +856,115 @@ free_orglist(struct prog_info *pi) pi->last_orglist = NULL; } +int +get_preproc_macro(struct prog_info *pi,char *name,struct preproc_macro **pm) +{ + struct preproc_macro *macro; + macro = search_preproc_macro(pi, pi->first_preproc_macro, name, NULL); + if (macro == NULL) return False; + if (pm != NULL) *pm = macro; + return True; +} + +struct preproc_macro * +test_preproc_macro(struct prog_info *pi,char *name,char *message) +{ + return search_preproc_macro(pi, pi->first_preproc_macro, name, message); +} + +struct preproc_macro * +search_preproc_macro(struct prog_info *pi,struct preproc_macro *first,char *name,char *message) +{ + struct preproc_macro *macro; + for (macro = first; macro; macro = macro->next) + if (!nocase_strcmp(macro->name, name)) { + if (message) { + print_msg(pi, MSGTYPE_ERROR, message, name); + } + return (macro); + } + return (NULL); +} + +int +def_preproc_macro(struct prog_info *pi, char *name, int type, struct item_list *params, char *value) { + struct preproc_macro *macro; + macro = malloc(sizeof(struct preproc_macro)); + if (!macro) { + print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); + return (False); + } + memset(macro, 0, sizeof(struct preproc_macro)); + macro->name = malloc(strlen(name)+1); + if (!macro->name) { + free(macro); + print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); + return (False); + } + strcpy(macro->name, name); + macro->type = type; + macro->params = params; + macro->value = malloc(strlen(value)+1); + if (!macro->value) { + free(macro->name); + free(macro); + print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); + return (False); + } + strcpy(macro->value, value); + + if (pi->last_preproc_macro) + pi->last_preproc_macro->next = macro; + else + pi->first_preproc_macro = macro; + pi->last_preproc_macro = macro; + return (True); +} + +void +free_preproc_macros(struct prog_info *pi) +{ + struct preproc_macro *macro, *temp_macro; + + for (macro = pi->first_preproc_macro; macro;) { + temp_macro = macro; + macro = macro->next; + + free(temp_macro->value); + if (temp_macro->type == PREPROC_MACRO_FUNCTION_LIKE) + free_item_list(temp_macro->params); + free(temp_macro->name); + free(temp_macro); + } + + pi->first_preproc_macro = NULL; + pi->last_preproc_macro = NULL; +} + +/* Determine the length of an item list. */ +int +item_list_length(struct item_list *lst) +{ + int len = 0; + while (lst) { + len++; + lst = lst->next; + } + return (len); +} + +/* Free up an item list. */ +void +free_item_list(struct item_list *lst) +{ + struct item_list *temp_lst; + while (lst) { + temp_lst = lst; + lst = lst->next; + free(temp_lst->value); + free(temp_lst); + } +} /* avra.c */ diff --git a/src/avra.h b/src/avra.h index 363d91f..bdac2a1 100644 --- a/src/avra.h +++ b/src/avra.h @@ -110,7 +110,24 @@ enum { TERM_EQUAL, TERM_DASH, TERM_DOUBLEQUOTE, - TERM_COLON + TERM_COLON, + TERM_CLOSING_PAREN +}; + +enum { + PREPROCESS_FATAL_ERROR = 0, + PREPROCESS_NEXT_LINE, + PREPROCESS_PARSE_LINE +}; + +enum { + PREPROC_MACRO_OBJECT_LIKE = 0, + PREPROC_MACRO_FUNCTION_LIKE +}; + +enum { + PREPROC_LINE_PART_WHITE = 0, + PREPROC_LINE_PART_GREY }; /* Structures */ @@ -157,6 +174,8 @@ struct prog_info { int error_count; int max_errors; int warning_count; + struct preproc_macro *first_preproc_macro; + struct preproc_macro *last_preproc_macro; struct include_file *last_include_file; struct include_file *first_include_file; struct def *first_def; @@ -273,6 +292,19 @@ struct location { int file_num; }; +struct item_list { + struct item_list *next; + char *value; +}; + +struct preproc_macro { + struct preproc_macro *next; + char *name; + int type; + struct item_list* params; + char *value; +}; + /* Prototypes */ /* avra.c */ int assemble(struct prog_info *pi); @@ -313,12 +345,27 @@ void free_ifdef_blacklist(struct prog_info *pi); void free_ifndef_blacklist(struct prog_info *pi); void free_variables(struct prog_info *pi); void free_orglist(struct prog_info *pi); +int get_preproc_macro(struct prog_info *pi,char *name,struct preproc_macro **pm); +struct preproc_macro *test_preproc_macro(struct prog_info *pi,char *name,char *message); +struct preproc_macro *search_preproc_macro(struct prog_info *pi,struct preproc_macro *first,char *name,char *message); +int def_preproc_macro(struct prog_info *pi, char *name, int type, struct item_list *params, char *value); +void free_preproc_macros(struct prog_info *pi); +int item_list_length(struct item_list *lst); +void free_item_list(struct item_list *lst); /* parser.c */ int parse_file(struct prog_info *pi, const char *filename); +int preprocess_line(struct prog_info *pi, char *line); int parse_line(struct prog_info *pi, char *line); char *get_next_token(char *scratch, int term); char *fgets_new(struct prog_info *pi, char *s, int size, FILE *stream); +char *funcall_token(char *token); +char *locate_macro_call(char *line, char *name, char **end); +char *locate_funcall_expr(char *line, char *name, char **end); +int is_label(char *word); +int collect_paramarg(struct prog_info *pi, char *paramarg, struct item_list **first_paramarg, struct item_list **last_paramarg); +int inplace_replace(char *line, char *begin, char *end, char *value, int buff_len); +void apply_preproc_macro_opers(char *line); /* expr.c */ int get_expr(struct prog_info *pi, char *data, int *value); @@ -378,6 +425,7 @@ int atoi_n(char *s, int n); int atox_n(char *s, int n); char *my_strlwr(char *in); char *my_strupr(char *in); +char *strdup_section(char *begin, char *end); char *snprint_list(char *buf, size_t limit, const char *const list[]); /* coff.c */ diff --git a/src/directiv.c b/src/directiv.c index 5627516..b1745da 100644 --- a/src/directiv.c +++ b/src/directiv.c @@ -533,45 +533,6 @@ parse_directive(struct prog_info *pi) if (test_constant(pi,next,"%s have already been defined as a .EQU constant")!=NULL) return (True); return (def_var(pi, next, i)); - case DIRECTIVE_DEFINE: - if (!next) { - print_msg(pi, MSGTYPE_ERROR, ".DEFINE needs an operand"); - return (True); - } - data = get_next_token(next, TERM_SPACE); - if (data) { - get_next_token(data, TERM_END); - if (!get_expr(pi, data, &i)) - return (False); - } else - i = 1; - if (test_label(pi,next,"%s have already been defined as a label")!=NULL) - return (True); - if (test_variable(pi,next,"%s have already been defined as a .SET variable")!=NULL) - return (True); - /* Forward references allowed. But check, if everything is ok ... */ - if (pi->pass==PASS_1) { /* Pass 1 */ - if (test_constant(pi,next,"Can't redefine constant %s, use .SET instead")!=NULL) - return (True); - if (def_const(pi, next, i)==False) - return (False); - } else { /* Pass 2 */ - int j; - if (get_constant(pi, next, &j)==False) { /* Defined in Pass 1 and now missing ? */ - print_msg(pi, MSGTYPE_ERROR, "Constant %s is missing in pass 2", next); - return (False); - } - if (i != j) { - print_msg(pi, MSGTYPE_ERROR, "Constant %s changed value from %d in pass1 to %d in pass 2", next,j,i); - return (False); - } - /* OK. Definition is unchanged */ - } - if ((pi->pass == PASS_2) && pi->list_line && pi->list_on) { - fprintf(pi->list_file, " %s\n", pi->list_line); - pi->list_line = NULL; - } - break; case DIRECTIVE_NOOVERLAP: if (pi->pass == PASS_1) { fix_orglist(pi->segment); @@ -640,7 +601,7 @@ parse_directive(struct prog_info *pi) get_next_token(next, TERM_END); /* Store location of ifdef (line number and file number) if the condition * fails on pass 1 so that we do not reinterpret it as succeeding on pass 2. */ - if ((pi->pass==PASS_1 && get_symbol(pi, next, NULL)) || (pi->pass==PASS_2 && !ifdef_is_blacklisted(pi))) { + if ((pi->pass==PASS_1 && (get_preproc_macro(pi, next, NULL) || get_symbol(pi, next, NULL))) || (pi->pass==PASS_2 && !ifdef_is_blacklisted(pi))) { pi->conditional_depth++; } else { if (pi->pass==PASS_1) { @@ -662,7 +623,7 @@ parse_directive(struct prog_info *pi) get_next_token(next, TERM_END); /* Store location of ifndef (line number and file number) if the condition * fails on pass 1 so that we do not reinterpret it as succeeding on pass 2. */ - if ((pi->pass==PASS_1 && !get_symbol(pi, next, NULL)) || (pi->pass==PASS_2 && !ifndef_is_blacklisted(pi))) { + if ((pi->pass==PASS_1 && (!get_preproc_macro(pi, next, NULL) && !get_symbol(pi, next, NULL))) || (pi->pass==PASS_2 && !ifndef_is_blacklisted(pi))) { pi->conditional_depth++; } else { if (pi->pass==PASS_1) { diff --git a/src/expr.c b/src/expr.c index 8d8baea..3fa202f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -470,7 +470,7 @@ get_expr(struct prog_info *pi, char *data, int *value) break; } data[i + length++] = '\0'; - if (get_symbol(pi, &data[i], NULL)) + if (get_preproc_macro(pi, &data[i], NULL) || get_symbol(pi, &data[i], NULL)) element->data = 1; else element->data = 0; diff --git a/src/parser.c b/src/parser.c index 710f1d6..08fb6ed 100644 --- a/src/parser.c +++ b/src/parser.c @@ -151,9 +151,9 @@ parse_file(struct prog_info *pi, const char *filename) fi->line_number++; pi->list_line = fi->buff; ok = parse_line(pi, fi->buff); -#if debug == 1 +/*#if debug == 1 printf("parse_line was %i\n", ok); -#endif +#endif*/ if (ok) { if ((pi->pass == PASS_2) && pi->list_line && pi->list_on) fprintf(pi->list_file, " %s\n", pi->list_line); @@ -177,6 +177,215 @@ parse_file(struct prog_info *pi, const char *filename) return (ok); } +/* Preprocess one line. */ +int +preprocess_line(struct prog_info *pi, char *line) +{ + char *ptr, *next, *data, *param, *next_param; + int params_cnt, args_cnt, macro_expanded; + struct item_list *params, *first_param, *last_param, *first_arg, *last_arg, *args_ptr; + struct preproc_macro *macro; + char *macro_begin, *macro_end, *par_begin, *par_end; + char *arg_mem, *arg, *next_arg; + char temp[LINEBUFFER_LENGTH]; + + do { + macro_expanded = False; + ptr = line; + + while (IS_HOR_SPACE(*ptr)) ptr++; + if (IS_END_OR_COMMENT(*ptr)) + return (PREPROCESS_NEXT_LINE); + if (ptr != line) + memmove(line, ptr, line+LINEBUFFER_LENGTH-1-ptr); + strcpy(temp, line); + + if (*temp == '#') { + next = get_next_token(temp, TERM_SPACE); + if (!nocase_strcmp(temp+1, "define")) { /* #define */ + /* #define name [value] */ + /* #define name(arg, ...) [value] */ + if (!next) { + print_msg(pi, MSGTYPE_ERROR, "#define needs an operand"); + return (PREPROCESS_NEXT_LINE); + } + /* Is it an object-like or a function-like macro? */ + param = funcall_token(next); + if (!param) { + /* Object-like macro definition */ + data = get_next_token(next, TERM_SPACE); + if (!data) + data = "1"; /* No value part provided, defaults to 1 */ + else + get_next_token(data, TERM_END); +#if debug == 1 + printf("preprocess_line obj next \"%s\" data \"%s\"\n", next, data); +#endif + if (pi->pass == PASS_1) { + if (test_preproc_macro(pi, next, "Preprocessor macro %s has already been defined") != NULL) + return (PREPROCESS_NEXT_LINE); + if (def_preproc_macro(pi, next, PREPROC_MACRO_OBJECT_LIKE, NULL, data) == False) + return (PREPROCESS_FATAL_ERROR); + } else { + /* Pass 2 */ + } + } else { + /* Function-like macro definition */ +#if debug == 1 + printf("preprocess_line fun next \"%s\"\n", next); +#endif + /* Collect params */ + first_param = last_param = NULL; + while ((next_param = get_next_token(param, TERM_COMMA))) { +#if debug == 1 + printf("preprocess_line fun param \"%s\"\n", param); +#endif + if (!is_label(param)) { + print_msg(pi, MSGTYPE_ERROR, "Function-like macro %s with invalid parameter %s", next, param); + return (PREPROCESS_NEXT_LINE); + } + if (!collect_paramarg(pi, param, &first_param, &last_param)) + return (PREPROCESS_FATAL_ERROR); + param = next_param; + } + data = get_next_token(param, TERM_CLOSING_PAREN); + if (!data) { + print_msg(pi, MSGTYPE_ERROR, "Fuction-like macro %s misses value", next); + return (PREPROCESS_NEXT_LINE); + } else + get_next_token(data, TERM_END); + if (!*param) { + print_msg(pi, MSGTYPE_ERROR, "Function-like macro %s misses a parameter", next); + return (PREPROCESS_NEXT_LINE); + } +#if debug == 1 + printf("preprocess_line fun param \"%s\"\n", param); + printf("preprocess_line fun data \"%s\"\n", data); +#endif + if (!is_label(param)) { + print_msg(pi, MSGTYPE_ERROR, "Function-like macro %s with invalid parameter %s", next, param); + return (PREPROCESS_NEXT_LINE); + } + if (!collect_paramarg(pi, param, &first_param, &last_param)) + return (PREPROCESS_FATAL_ERROR); + if (pi->pass == PASS_1) { + if (test_preproc_macro(pi, next, "Preprocessor macro %s has already been defined") != NULL) + return (PREPROCESS_NEXT_LINE); + if (def_preproc_macro(pi, next, PREPROC_MACRO_FUNCTION_LIKE, first_param, data) == False) + return (PREPROCESS_FATAL_ERROR); + } else { + /* Pass 2 */ + } + } + if ((pi->pass == PASS_2) && pi->list_line && pi->list_on) { + fprintf(pi->list_file, " %s\n", pi->list_line); + pi->list_line = NULL; + } + return (PREPROCESS_NEXT_LINE); /* #define successfully parsed, continue with next line */ + } else if (!nocase_strcmp(temp+1, "undef")) { /* #undef */ + print_msg(pi, MSGTYPE_ERROR, "#undef is not supported at the moment"); + return (PREPROCESS_NEXT_LINE); + } else { + /* Rest of the preprocessor macros to be handled by the original code */ + } + } else { + /* Expand preprocessor macros */ + for (macro = pi->first_preproc_macro; macro;) { + if (macro->type == PREPROC_MACRO_OBJECT_LIKE) { + /* Object-like macro */ + while ((macro_begin = locate_macro_call(line, macro->name, ¯o_end))) { + if (!inplace_replace(line, macro_begin, macro_end, macro->value, LINEBUFFER_LENGTH)) { + print_msg(pi, MSGTYPE_ERROR, "Expanded macro %s with value %s too big", macro->name, macro->value); + return (PREPROCESS_NEXT_LINE); + } + macro_expanded = True; + } + } else { + /* Function-like macro */ + while ((macro_begin = locate_funcall_expr(line, macro->name, ¯o_end))) { + if (pi->pass == PASS_2 && pi->list_line && pi->list_on) { + fprintf(pi->list_file, "%c:%06lx + %s\n", + pi->cseg->ident, pi->cseg->addr, pi->list_line); + // pi->list_line = NULL; + } + arg_mem = arg = strdup_section(macro_begin + strlen(macro->name) +1, macro_end-1); + if (!arg_mem) { + print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); + return (PREPROCESS_FATAL_ERROR); + } +#if debug == 1 + printf("preprocess_line arg \"%s\"\n", arg); +#endif + /* Collect args */ + first_arg = last_arg = NULL; + while ((next_arg = get_next_token(arg, TERM_COMMA))) { +#if debug == 1 + printf("preprocess_line arg 1+ \"%s\"\n", arg); +#endif + if (!is_label(arg)) { + print_msg(pi, MSGTYPE_ERROR, "Function-like macro %s with invalid argument %s", macro->name, arg); + free(arg_mem); + return (PREPROCESS_NEXT_LINE); + } + if (!collect_paramarg(pi, arg, &first_arg, &last_arg)) + return (PREPROCESS_FATAL_ERROR); + arg = next_arg; + } + get_next_token(arg, TERM_END); +#if debug == 1 + printf("preprocess_line arg 1 \"%s\"\n", arg); +#endif + if (!collect_paramarg(pi, arg, &first_arg, &last_arg)) + return (PREPROCESS_FATAL_ERROR); + free(arg_mem); + /* Parameter vs. argument list length check */ + params_cnt = item_list_length(macro->params); + args_cnt = item_list_length(first_arg); + if (params_cnt != args_cnt) { + print_msg(pi, MSGTYPE_ERROR, "Function-like macro %s called with %d arguments, however is defined with %d parameters", macro->name, args_cnt, params_cnt); + return (PREPROCESS_NEXT_LINE); + } + strcpy(temp, macro->value); +#if debug == 1 + printf("preprocess_line temp \"%s\"\n", temp); +#endif + for (params = macro->params, args_ptr = first_arg; params; params = params->next, args_ptr = args_ptr->next) { + while ((par_begin = locate_macro_call(temp, params->value, &par_end))) { + if (!inplace_replace(temp, par_begin, par_end, args_ptr->value, LINEBUFFER_LENGTH)) { + print_msg(pi, MSGTYPE_ERROR, "Expanded macro parameter %s with value %s too big", params->value, args_ptr->value); + return (PREPROCESS_NEXT_LINE); + } +#if debug == 1 + printf("preprocess_line temp \"%s\"\n", temp); +#endif + } + } + free_item_list(first_arg); +#if debug == 1 + printf("preprocess_line line (before inplace_replace) \"%s\"\n", line); +#endif + if (!inplace_replace(line, macro_begin, macro_end, temp, LINEBUFFER_LENGTH)) { + print_msg(pi, MSGTYPE_ERROR, "Expanded macro %s with value %s too big", macro->name, temp); + return (PREPROCESS_NEXT_LINE); + } +#if debug == 1 + printf("preprocess_line line (after inplace_replace) \"%s\"\n", line); +#endif + apply_preproc_macro_opers(line); +#if debug == 1 + printf("preprocess_line line (after apply_preproc_macro_opers) \"%s\"\n", line); +#endif + macro_expanded = True; + } + } + macro = macro->next; + } + } + + } while (macro_expanded); + return (PREPROCESS_PARSE_LINE); +} + /* Parse one line. */ int parse_line(struct prog_info *pi, char *line) @@ -190,17 +399,25 @@ parse_line(struct prog_info *pi, char *line) struct macro_call *macro_call; int len; - while (IS_HOR_SPACE(*line)) line++; /* At first remove leading spaces / tabs */ - if (IS_END_OR_COMMENT(*line)) /* Skip comment line or empty line */ - return (True); + /* Preprocess the line. */ + switch (preprocess_line(pi, line)) { + case PREPROCESS_PARSE_LINE: + break; + case PREPROCESS_NEXT_LINE: + return (True); + default: + case PREPROCESS_FATAL_ERROR: + return (False); + } + /* Filter out .stab debugging information */ /* .stabs sometimes contains colon : symbol - might be interpreted as label */ if (*line == '.') { /* minimal slowdown of existing code */ - if (strncmp(temp,".stabs ",7) == 0) { /* compiler output is always lower case */ + if (strncmp(line,".stabs ",7) == 0) { /* compiler output is always lower case */ strcpy(temp,line); /* TODO : Do we need this temp variable ? Please check */ return parse_stabs(pi, temp); } - if (strncmp(temp,".stabn ",7) == 0) { + if (strncmp(line,".stabn ",7) == 0) { strcpy(temp,line); return parse_stabn(pi, temp); } @@ -361,6 +578,10 @@ get_next_token(char *data, int term) /* Skip to next equals or EOL or start of comment. */ while ((data[i] != '=') && !IS_END_OR_COMMENT(data[i])) i++; break; + case TERM_CLOSING_PAREN: + /* Skip to next ) or EOL or start of comment. */ + while ((data[i] != ')') && !IS_END_OR_COMMENT(data[i])) i++; + break; } /* If we hit EOL or a comment, return null. */ if (IS_END_OR_COMMENT(data[i])) { @@ -386,5 +607,162 @@ get_next_token(char *data, int term) return (&data[i]); } +/* Check whether the pointed token is a function call. + * If so, terminate the name and return a pointer to the next token or end. */ +char * +funcall_token(char *token) { + if (*token != '%' && *token != '_' && !isalpha(*token)) return NULL; + while (IS_LABEL(*token)) token++; + if (*token == '(') { + *token++ = '\0'; /* Null-out paren, so we get the name */ + while (IS_HOR_SPACE(*token)) token++; /* Skip whitespace after paren */ + return (token); /* Return a pointer to the next token or end */ + } + return (NULL); +} + +/* Locate the first occurence of a macro call in a line. + * Intended to be called repetitively to exhaust all possibilities. + * Returns NULL in case there is no occurence. + * Otherwise returns the pointer to the beginning of the macro call and sets the end ptr.*/ +char * +locate_macro_call(char *line, char *name, char **end) +{ + char *macro_call, *macro_call_end, *comment, *ptr; + char *funcall_begin, *funcall_end; + int name_len, inside_defined; + comment = strchr(line, ';'); + name_len = strlen(name); + while (1) { + macro_call = nocase_strstr(line, name); + if (!macro_call) + return (NULL); + if (comment && comment < macro_call) + return (NULL); + macro_call_end = macro_call + name_len - 1; + if (macro_call == line || IS_END_OR_COMMENT(*(macro_call_end + 1))) + break; + if (IS_LABEL(*(macro_call - 1)) || IS_LABEL(*(macro_call_end + 1))) { + while (IS_LABEL(*macro_call_end)) macro_call_end++; + line = macro_call_end; + continue; + } + ptr = line; + inside_defined = False; + while (!inside_defined && + (funcall_begin = locate_funcall_expr(ptr, "defined", &funcall_end))) { + if (funcall_begin < macro_call && macro_call_end < funcall_end) + inside_defined = True; + ptr = funcall_end + 1; + } + if (!inside_defined) + break; + line = funcall_end + 1; + } + if (end) *end = macro_call_end; + return (macro_call); +} + +/* Locate the first occurence of the function call expression in a line. + * Intended to be called repetitively to exhaust all possibilities. + * Returns NULL in case there is no occurence. + * Otherwise returns the pointer to the beginning of the expr. and sets the end ptr.*/ +char * +locate_funcall_expr(char *line, char *name, char **end) +{ + char *expr, *expr_end, *comment, *ptr; + int len; + expr = nocase_strstr(line, name); + if (expr) { + ptr = expr + strlen(name); + if (*ptr == '(') { + len = par_length(ptr + 1); + if (len >= 0) { + expr_end = ptr + 1 + len; + comment = strchr(line, ';'); + if (!comment || expr_end < comment) { + if (expr == line || !IS_LABEL(*(expr - 1))) { + if (end) *end = expr_end; + return (expr); + } + } + } + } + } + return (NULL); +} + +/* Checks whether the given word is a label. + * Returns true, when it is a label. False otherwise. */ +int +is_label(char *word) +{ + if (word == NULL) + return (False); + do { + if (!IS_LABEL(*word)) + return (False); + } while(*++word); + return (True); +} + +/* Collect parameters or arguments into the supplied list. + * Signals error and returns False in case of memory full. */ +int +collect_paramarg(struct prog_info *pi, char *paramarg, struct item_list **first_paramarg, struct item_list **last_paramarg) +{ + struct item_list *new_paramarg; + new_paramarg = malloc(sizeof(struct item_list)); + if (!new_paramarg) { + print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); + return (False); + } + new_paramarg->next = NULL; + new_paramarg->value = malloc(strlen(paramarg)+1); + if (!new_paramarg->value) { + print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); + return (False); + } + strcpy(new_paramarg->value, paramarg); + + if (*last_paramarg) + (*last_paramarg)->next = new_paramarg; + else + *first_paramarg = new_paramarg; + *last_paramarg = new_paramarg; + return (True); +} + +/* Replace the range of characters in line between begin and end with value. + * Returns False in case the result would not fit into the line buffer. */ +int +inplace_replace(char *line, char *begin, char *end, char *value, int buff_len) +{ + int value_len, rest_len, range_len; + value_len = strlen(value); + rest_len = strlen(end + 1); + range_len = end - begin +1; + if (strlen(line) +1 - range_len + value_len > buff_len) + return (False); + if (range_len != value_len) + memmove(begin + value_len, end + 1, rest_len + 1); + memmove(begin, value, value_len); + return (True); +} + +/* Apply the preprocessor macro operators. */ +void +apply_preproc_macro_opers(char *line) +{ + char *begin, *end; + + while ((begin = strstr(line, "##"))) { /* Apply Concatenation (##) operator */ + end = begin + 2; + while (begin - 1 > line && IS_HOR_SPACE(*(begin - 1))) begin--; + while (IS_HOR_SPACE(*end)) end++; + memmove(begin, end, strlen(end) + 1); + } +} + /* end of parser.c */ diff --git a/src/stdextra.c b/src/stdextra.c index 968618e..238c83c 100644 --- a/src/stdextra.c +++ b/src/stdextra.c @@ -27,6 +27,8 @@ #include #include +#include +#include #include "misc.h" @@ -157,6 +159,22 @@ my_strupr(char *in) return (in); } +/* Create a dynamically allocated duplicate of the section pointed by begin and end. + * Returns the pointer the duplicate or NULL when no memory. + * Begin and end must be pointers to a section within the same string. */ +char * +strdup_section(char *begin, char *end) +{ + char *dup; + size_t len = end - begin +1; + dup = malloc(len +1); + if (!dup) + return (NULL); + strncpy(dup, begin, len); + dup[len] = '\0'; + return (dup); +} + static int snprint(char **buf, size_t *limit, const char *const str) { diff --git a/tests/regression/preprocessor-macros/test.asm b/tests/regression/preprocessor-macros/test.asm new file mode 100644 index 0000000..422ec2e --- /dev/null +++ b/tests/regression/preprocessor-macros/test.asm @@ -0,0 +1,16 @@ +.device atmega328p + +#define EIGHT (1 << 3) +#define SQR(X) ((X)*(X)) + +ldi r16, EIGHT +ldi r16, SQR(4) + +#define FOOBAR subi +#define IMMED(X) X##i +#define SUBI(X,Y) X ## Y + +IMMED(ld) r16, 1 +SUBI(FOO, BAR) r16, 1 + +IMMED(ld) r16, SQR(SQR(1+1)) diff --git a/tests/regression/preprocessor-macros/test.hex.expected b/tests/regression/preprocessor-macros/test.hex.expected new file mode 100644 index 0000000..e6faf4d --- /dev/null +++ b/tests/regression/preprocessor-macros/test.hex.expected @@ -0,0 +1,3 @@ +:020000020000FC +:0A00000008E000E101E0015000E11A +:00000001FF