/* 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 . ------------------------------------------------------------------------- Collection for macros. */ #include #include #include #include #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 { if (in_num == 0 && line[rd] == '*') { int f; rd++; for(f = 0; f < macro->argc; f++) { if (f > 0) { VarcharAddChar(str, ','); } AddArg(str, macro, f); } in_num = -1; } 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 */