diff options
Diffstat (limited to 'src/macro.c')
-rw-r--r-- | src/macro.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/src/macro.c b/src/macro.c new file mode 100644 index 0000000..03891c2 --- /dev/null +++ b/src/macro.c @@ -0,0 +1,509 @@ +/* + + casm - Simple, portable assembler + + Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Collection for macros. + +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "global.h" +#include "codepage.h" +#include "varchar.h" +#include "macro.h" + + +/* ---------------------------------------- TYPES +*/ + +enum option_t +{ + OPT_MACROARGCHAR +}; + +static const ValueTable option_set[] = +{ + {"macro-arg-char", OPT_MACROARGCHAR}, + {NULL} +}; + + +struct mdef +{ + char *name; + int no_args; + char **args; + int no_lines; + char **lines; + struct mdef *next; +}; + + +struct macro +{ + MacroDef *def; + int line; + int argc; + char **argv; + int *quoted; +}; + + +/* ---------------------------------------- GLOBALS +*/ +static MacroDef *head; +static MacroDef *tail; + +static const char *arg_chars = "ABCDEFGHIJKLMNOPQRSTUVXYZ" + "abcdefghijklmnopqrstuvxyz" + "0123456789_"; + +static struct +{ + int arg_char; +} options = +{ + '@' +}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS INTERFACES +*/ +static MacroDef *FindMacro(const char *p) +{ + MacroDef *m = head; + + while(m) + { + if (CompareString(m->name, p)) + { + return m; + } + + m = m->next; + } + + return NULL; +} + + +static int CheckArgName(const char *p) +{ + while(*p) + { + if (!strchr(arg_chars, *p++)) + { + return FALSE; + } + } + + return TRUE; +} + + +static MacroDef *AddMacro(const char *p, int argc, char *argv[], + char *err, size_t errsize) +{ + MacroDef *m = FindMacro(p); + + if (!m) + { + m = Malloc(sizeof *m); + + m->name = DupStr(p); + m->no_lines = 0; + m->lines = NULL; + m->next = NULL; + + m->no_args = argc; + + if (m->no_args == 0) + { + m->args = NULL; + } + else + { + int f; + + m->args = Malloc((sizeof *m->args) * m->no_args); + + for(f = 0; f < argc; f++) + { + if (!argv[f][0] || !CheckArgName(argv[f])) + { + snprintf(err, errsize, "illegal argument name '%s'", + argv[f]); + return NULL; + } + + m->args[f] = DupStr(argv[f]); + } + } + + if (tail) + { + tail->next = m; + } + + tail = m; + + if (!head) + { + head = m; + } + } + else + { + snprintf(err, errsize, "macro %s already exists", p); + m = NULL; + } + + return m; +} + + +static void AddArg(Varchar *str, Macro *macro, int arg_no) +{ + if (arg_no < macro->argc) + { + int quote = macro->quoted[arg_no]; + + if (quote) + { + VarcharAddChar(str, quote); + } + + VarcharAdd(str, macro->argv[arg_no]); + + if (quote) + { + if (quote == '(') + { + VarcharAddChar(str, ')'); + } + else + { + VarcharAddChar(str, quote); + } + } + } +} + + +static int FindArg(MacroDef *def, const char *name) +{ + int f; + + for(f = 0; f < def->no_args; f++) + { + if (CompareString(def->args[f], name)) + { + return f; + } + } + + return def->no_args; +} + +/* ---------------------------------------- INTERFACES +*/ + +void MacroSetDefaults(void) +{ + options.arg_char = '@'; +} + +const ValueTable *MacroOptions(void) +{ + return option_set; +} + +CommandStatus MacroSetOption(int opt, int argc, char *argv[], + int quoted[], char *err, size_t errsize) +{ + CommandStatus stat = CMD_OK; + + CMD_ARGC_CHECK(1); + + switch(opt) + { + case OPT_MACROARGCHAR: + options.arg_char = argv[0][0]; + + if (options.arg_char == 0) + { + snprintf(err, errsize, "illegal character '\\0'"); + stat = CMD_FAILED; + } else if (isalnum((unsigned char)options.arg_char)) + { + snprintf(err, errsize, "illegal character '%c'", + options.arg_char); + stat = CMD_FAILED; + } + break; + + default: + break; + } + + return stat; +} + + +MacroDef *MacroCreate(const char *name, int argc, char *argv[], + char *err, size_t errsize) +{ + MacroDef *macro = NULL; + + macro = AddMacro(name, argc, argv, err, errsize); + + return macro; +} + +void MacroRecord(MacroDef *macro, const char *line) +{ + if (macro) + { + macro->no_lines++; + + macro->lines = Realloc(macro->lines, macro->no_lines * + sizeof *macro->lines); + macro->lines[macro->no_lines - 1] = DupStr(line); + } +} + +CommandStatus MacroFind(Macro **ret, int argc, char *argv[], int quoted[], + char *err, size_t errsize) +{ + MacroDef *def = NULL; + Macro *macro = NULL; + CommandStatus status = CMD_NOT_KNOWN; + + if (argc > 0) + { + def = FindMacro(argv[0]); + } + + if (def) + { + if (def->no_args && def->no_args != argc - 1) + { + snprintf(err, errsize, "%s: expected %d argument%s, got %d", + argv[0], def->no_args, + def->no_args == 1 ? "" : "s", argc - 1); + + status = CMD_FAILED; + } + else + { + int f; + + macro = Malloc(sizeof *macro); + macro->line = 0; + macro->def = def; + macro->argc = argc; + macro->argv = Malloc(argc * sizeof *macro->argv); + macro->quoted = Malloc(argc * sizeof *macro->quoted); + + for(f = 0; f < argc; f++) + { + macro->argv[f] = DupStr(argv[f]); + macro->quoted[f] = quoted[f]; + } + + status = CMD_OK; + } + } + + *ret = macro; + + return status; +} + + +char *MacroPlay(Macro *macro) +{ + if (macro && macro->line < macro->def->no_lines) + { + int size = 1000; + int rd; + char num[64]; + char arg[128]; + const char *line; + int in_num = -1; + int in_arg = -1; + Varchar *str; + + line = macro->def->lines[macro->line++]; + + /* If no arguments, simply duplicate + */ + if (!strchr(line, '\\') && !strchr(line, options.arg_char)) + { + return DupStr(line); + } + + /* Expand the arguments + */ + str = VarcharCreate(NULL); + rd = 0; + + while(line[rd]) + { + if (line[rd] == '\\') + { + in_num = 0; + rd++; + } + else if (line[rd] == options.arg_char) + { + in_arg = 0; + rd++; + } + else + { + if (in_num != -1) + { + if (isdigit((unsigned char)line[rd]) && in_num < 60) + { + num[in_num++] = line[rd++]; + } + else + { + num[in_num] = 0; + AddArg(str, macro, atoi(num)); + in_num = -1; + } + } + else if (in_arg != -1) + { + if (strchr(arg_chars, line[rd]) && in_arg < 125) + { + arg[in_arg++] = line[rd++]; + } + else + { + arg[in_arg] = 0; + AddArg(str, macro, FindArg(macro->def, arg) + 1); + in_arg = -1; + } + } + else + { + VarcharAddChar(str, line[rd++]); + } + } + } + + /* Check for arguments at the end of the line + */ + if (in_num != -1) + { + num[in_num] = 0; + AddArg(str, macro, atoi(num)); + in_num = -1; + } + + if (in_arg != -1) + { + arg[in_arg] = 0; + AddArg(str, macro, FindArg(macro->def, arg) + 1); + in_arg = -1; + } + + return VarcharTransfer(str); + } + + return NULL; +} + + +void MacroFree(Macro *macro) +{ + if (macro) + { + int f; + + for(f = 0; f < macro->argc; f++) + { + free(macro->argv[f]); + } + + free(macro->quoted); + free(macro->argv); + free(macro); + } +} + + +const char *MacroName(Macro *macro) +{ + if (macro) + { + return macro->def->name; + } + + return "(unknown)"; +} + + +void MacroDump(FILE *fp) +{ + MacroDef *m = head; + + while(m) + { + int f; + + fprintf(fp, "; %s: MACRO", m->name); + + if (m->no_args) + { + for(f = 0; f < m->no_args; f++) + { + fprintf(fp, "%s%s", f == 0 ? " " : ", ", m->args[f]); + } + } + + putc('\n', fp); + + for(f = 0; f < m->no_lines; f++) + { + fprintf(fp, "; %s\n", m->lines[f]); + } + + fprintf(fp, "; ENDM\n"); + + m = m->next; + + if (m) + { + fprintf(fp, ";\n"); + } + } +} + + +/* +vim: ai sw=4 ts=8 expandtab +*/ |