Added MASM-style macro support
authorRobert Pengelly <robertapengelly@hotmail.com>
Fri, 11 Sep 2026 10:09:02 +0000 (11:09 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Fri, 11 Sep 2026 10:09:02 +0000 (11:09 +0100)
process.c

index cb77bb8bfec443409f7a58c80776fe191b6ebec2..56c1c8a255d200b383cc1d71524d6a3889fe3628 100644 (file)
--- a/process.c
+++ b/process.c
 
 static struct hashtab hashtab_seen_macros = { 0 };
 
+struct masm_macro {
+
+    char *name;
+    
+    struct vector args;
+    struct cstring body;
+
+};
+
+static struct vector vec_masm_macros = { 0 };
+static struct vector vec_active_masm_macros = { 0 };
+
+static int masm_macro_is_active (struct masm_macro *m) {
+
+    int i;
+    
+    for (i = 0; i < vec_active_masm_macros.length; i++) {
+    
+        if (vec_active_masm_macros.data[i] == m) {
+            return 1;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static struct masm_macro *find_masm_macro (const char *name) {
+
+    int i;
+    
+    for (i = 0; i < vec_masm_macros.length; i++) {
+    
+        struct masm_macro *m = vec_masm_macros.data[i];
+        
+        if (xstrcasecmp (m->name, name) == 0) {
+            return m;
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void free_masm_macro (struct masm_macro *m) {
+
+    char *arg;
+    
+    if (!m) {
+        return;
+    }
+    
+    while ((arg = vec_pop (&m->args))) {
+        free (arg);
+    }
+    
+    free (m->args.data);
+    free (m->name);
+    
+    cstr_free (&m->body);
+    free (m);
+
+}
+
+static int is_masm_endm (char *line, char *line_end) {
+
+    char *caret, *name;
+    int ret = 0;
+    
+    caret = skip_whitespace (line);
+    
+    if (caret >= line_end || !(name = symname (&caret))) {
+        return 0;
+    }
+    
+    caret = skip_whitespace (caret);
+    
+    if (xstrcasecmp (name, "endm") == 0 && (caret >= line_end || is_end_of_line[(int) *caret])) {
+        ret = 1;
+    }
+    
+    free (name);
+    return ret;
+
+}
+
+static struct masm_macro *parse_masm_macro_definition (char *start, char *line_end) {
+
+    char *line, *name, *directive, *arg;
+    struct masm_macro *m;
+    
+    line = skip_whitespace (start);
+    
+    if (line >= line_end || !(name = symname (&line))) {
+        return 0;
+    }
+    
+    line = skip_whitespace (line);
+    
+    if (line >= line_end || !(directive = symname (&line))) {
+    
+        free (name);
+        return 0;
+    
+    }
+    
+    if (xstrcasecmp (directive, "macro") != 0) {
+    
+        free (directive);
+        free (name);
+        
+        return 0;
+    
+    }
+    
+    free (directive);
+    
+    m = xmalloc (sizeof (*m));
+    memset (m, 0, sizeof (*m));
+    
+    m->name = name;
+    cstr_new (&m->body);
+    
+    line = skip_whitespace (line);
+    
+    while (line < line_end && !is_end_of_line[(int) *line]) {
+    
+        if (!(arg = symname (&line))) {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, line, "expected macro parameter name");
+            
+            free_masm_macro (m);
+            return (struct masm_macro *) -1;
+        
+        }
+        
+        vec_push (&m->args, arg);
+        line = skip_whitespace (line);
+        
+        if (line >= line_end || is_end_of_line[(int) *line]) {
+            break;
+        }
+        
+        if (*line != ',') {
+        
+            report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, line, "expected ',' after macro parameter");
+            
+            free_masm_macro (m);
+            return (struct masm_macro *) -1;
+        
+        }
+        
+        line = skip_whitespace (line + 1);
+    
+    }
+    
+    return m;
+
+}
+
+static int masm_macro_arg_index (struct masm_macro *m, const char *name) {
+
+    int i;
+    
+    for (i = 0; i < m->args.length; i++) {
+    
+        if (xstrcasecmp (m->args.data[i], name) == 0) {
+            return i;
+        }
+    
+    }
+    
+    return -1;
+
+}
+
+static void free_string_vector (struct vector *vec) {
+
+    char *arg;
+    
+    while ((arg = vec_pop (vec))) {
+        free (arg);
+    }
+    
+    free (vec->data);
+    memset (vec, 0, sizeof (*vec));
+
+}
+
+static int parse_masm_macro_arguments (char *start, char *line, struct masm_macro *m, struct vector *args) {
+
+    char *arg_start, *caret, ch;
+    
+    memset (args, 0, sizeof (*args));
+    line = skip_whitespace (line);
+    
+    while (!is_end_of_line[(int) *line]) {
+        
+        arg_start = line;
+        caret = line;
+        ch = 0;
+        
+        while (!is_end_of_line[(int) *caret]) {
+        
+            if (ch) {
+            
+                if (*caret == '\\' && caret[1]) {
+                
+                    caret += 2;
+                    continue;
+                
+                }
+                
+                if (*caret == ch) { ch = 0; }
+                caret++;
+                
+                continue;
+            
+            }
+            
+            if (*caret == '\"' || *caret == '\'') {
+            
+                ch = *caret++;
+                continue;
+            
+            }
+            
+            if (*caret == ',') { break; }
+            caret++;
+        
+        }
+        
+        while (caret > arg_start && isspace ((int) caret[-1])) {
+            caret--;
+        }
+        
+        while (arg_start < caret && isspace ((int) *arg_start)) {
+            arg_start++;
+        }
+        
+        vec_push (args, xstrndup (arg_start, caret - arg_start));
+        line = caret;
+        
+        if (*line == ',') {
+            line = skip_whitespace (line + 1);
+        }
+    
+    }
+    
+    if (args->length != m->args.length) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start),
+            "macro \"%s\" requires %d argument%s, but %d given",
+                m->name, m->args.length, m->args.length == 1 ? "" : "s", args->length);
+        return 1;
+    
+    }
+    
+    return 0;
+
+}
+
+static char *expand_masm_macro_body (struct masm_macro *m, struct vector *args) {
+
+    struct cstring out;
+    
+    char *line, *name;
+    int idx, last_was_param = 0;
+    
+    cstr_new (&out);
+    line = m->body.data;
+    
+    while (line && *line) {
+    
+        if (*line == '"' || *line == '\'') {
+        
+            char quote = *line;
+            cstr_ccat (&out, *line++);
+            
+            while (*line) {
+            
+                cstr_ccat (&out, *line);
+                
+                if (*line == '\\' && line[1]) {
+                
+                    line++;
+                    
+                    cstr_ccat (&out, *line++);
+                    continue;
+                
+                }
+                
+                if (*line++ == quote) { break; }
+            
+            }
+            
+            last_was_param = 0;
+            continue;
+        
+        }
+        
+        if (is_name_beginner ((int) *line)) {
+        
+            name = symname (&line);
+            idx = masm_macro_arg_index (m, name);
+            
+            if (idx >= 0) {
+            
+                cstr_cat (&out, args->data[idx], strlen (args->data[idx]));
+                last_was_param = 1;
+            
+            } else {
+            
+                cstr_cat (&out, name, strlen (name));
+                last_was_param = 0;
+            
+            }
+            
+            free (name);
+            continue;
+        
+        }
+        
+        if (*line == '&') {
+        
+            int concatenate = last_was_param;
+            
+            if (!concatenate && is_name_beginner ((int) line[1])) {
+            
+                char *temp = line + 1;
+                
+                name = symname (&temp);
+                concatenate = (masm_macro_arg_index (m, name) >= 0);
+                
+                free (name);
+            
+            }
+            
+            if (concatenate) {
+            
+                line++;
+                continue;
+            
+            }
+        
+        }
+        
+        cstr_ccat (&out, *line++);
+        last_was_param = 0;
+    
+    }
+    
+    cstr_ccat (&out, '\0');
+    return out.data;
+
+}
+
+static void install_masm_macro (struct masm_macro *m) {
+
+    int i;
+    
+    for (i = 0; i < vec_masm_macros.length; i++) {
+    
+        struct masm_macro *old = vec_masm_macros.data[i];
+        
+        if (xstrcasecmp (old->name, m->name) == 0) {
+        
+            report_at (get_filename (), get_line_number (), REPORT_WARNING, "macro \"%s\" redefined", m->name);
+            
+            free_masm_macro (old);
+            vec_masm_macros.data[i] = m;
+            
+            return;
+        
+        }
+    
+    }
+    
+    vec_push (&vec_masm_macros, m);
+
+}
+
 static char *preprocess_line (char *src, int in_macro) {
 
     struct cstring cstr;
@@ -1254,6 +1638,79 @@ static void process_line (char *line, char *line_end) {
 
 }
 
+static void process_source_line (char *start) {
+
+    char *line, *name;
+    
+    struct masm_macro *m;
+    struct vector args;
+    
+    line = skip_whitespace (start);
+    
+    if (is_name_beginner ((int) *line)) {
+    
+        char *after_name = line;
+        
+        if ((name = symname (&after_name))) {
+        
+            if ((m = find_masm_macro (name)) && !masm_macro_is_active (m)) {
+            
+                char *expanded, *p, *next;
+                after_name = skip_whitespace (after_name);
+                
+                if (!parse_masm_macro_arguments (start, after_name, m, &args)) {
+                
+                    expanded = expand_masm_macro_body (m, &args);
+                    vec_push (&vec_active_masm_macros, m);
+                    
+                    p = expanded;
+                    
+                    while (*p) {
+                    
+                        next = strchr (p, '\n');
+                        
+                        if (next) {
+                            *next = '\0';
+                        }
+                        
+                        if (*skip_whitespace (p)) {
+                            process_source_line (p);
+                        }
+                        
+                        if (!next) { break; }
+                        p = next + 1;
+                    
+                    }
+                    
+                    vec_pop (&vec_active_masm_macros);
+                    free (expanded);
+                
+                }
+                
+                free_string_vector (&args);
+                free (name);
+                
+                return;
+            
+            }
+            
+            free (name);
+        
+        }
+    
+    }
+    
+    {
+    
+        char *tokenized_line = preprocess_line (start, 0);
+        
+        process_line (tokenized_line, tokenized_line + strlen (tokenized_line));
+        free (tokenized_line);
+    
+    }
+
+}
+
 void process_file (const char *ifile) {
 
     char *start, *arg, *caret;
@@ -1270,6 +1727,8 @@ void process_file (const char *ifile) {
     struct cond *cond;
     int cond_idx, proc_idx, seg_idx;
     
+    struct masm_macro *masm_macro = 0;
+    
     if (!ifile || strcmp (ifile, "-") == 0) {
     
         set_filename_and_line_number (xstrdup ("<stdin>"), 0);
@@ -1308,6 +1767,43 @@ void process_file (const char *ifile) {
             printf ("%s:%lu: %.*s", get_filename (), get_line_number (), (int) real_line_len, real_line);
         }
         
+        if (masm_macro) {
+        
+            if (is_masm_endm (line, line_end)) {
+            
+                cstr_ccat (&masm_macro->body, '\0');
+                
+                install_masm_macro (masm_macro);
+                masm_macro = 0;
+            
+            } else {
+            
+                cstr_cat (&masm_macro->body, line, line_end - line);
+                cstr_ccat (&masm_macro->body, '\n');
+            
+            }
+            
+            continue;
+        
+        }
+        
+        if (!ignore_line) {
+        
+            struct masm_macro *new_macro = parse_masm_macro_definition (line, line_end);
+            
+            if (new_macro == (struct masm_macro *) -1) {
+                continue;
+            }
+            
+            if (new_macro) {
+            
+                masm_macro = new_macro;
+                continue;
+            
+            }
+        
+        }
+        
         caret = (line = skip_whitespace (line));
         
         if (!ignore_line && line >= line_end) {
@@ -1396,16 +1892,22 @@ void process_file (const char *ifile) {
         if (!ignore_line) {
         
             if (line < line_end) {
-            
-                char *tokenized_line = preprocess_line (line, 0);
-                process_line (tokenized_line, tokenized_line + strlen (tokenized_line));
-            
+                process_source_line (line);
             }
         
         }
     
     }
     
+    if (masm_macro) {
+    
+        report_at (get_filename (), get_line_number (), REPORT_ERROR, "unterminated macro \"%s\"", masm_macro->name);
+        
+        free_masm_macro (masm_macro);
+        masm_macro = 0;
+    
+    }
+    
     if (state->lfile) {
         update_listing_line (current_frag);
     }