projects/08/src/vmtranslator.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
#include <dirent.h> #include <libgen.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "codewriter.h" #include "parser.h" #include "util.h" #define _DEBUG #define MAX_FILE_PATH_LEN (4096) char *file_line; size_t file_line_no; static int is_directory(const char *path) { struct stat statbuf; if (stat(path, &statbuf) != 0) return 0; return S_ISDIR(statbuf.st_mode); } static int is_regular_file(const char *path) { struct stat statbuf; if (stat(path, &statbuf) != 0) return 0; return S_ISREG(statbuf.st_mode); } static const char *filename_ext(const char *filepath) { const char *pos = strrchr(filepath, '.'); if (!pos || pos == filepath) return NULL; return pos + 1; } static char *concat_filepath(const char *filename, const char *dirname) { char *filepath; size_t filename_len = strlen(filename); size_t dirname_len = strlen(dirname); if ((size_t)(dirname_len + filename_len) > (size_t) MAX_FILE_PATH_LEN) { printf("resulting file path name too long\n"); return NULL; } filepath = malloc(MAX_FILE_PATH_LEN); filepath[0] = '\0'; strncat(filepath, dirname, dirname_len); strncat(filepath, filename, filename_len); return filepath; } // sets symbol name (used for statc vars) to base name of file before extension // (note: this is basically just the filename w/o the extension) // TODO: rename this function to something clearer static bool set_static_symbol_name(char *filename) { size_t i, filename_len, base_name_len; char *tmp, *base_name; filename_len = strlen(filename); tmp = malloc(filename_len + 1); for (i = 0; i < filename_len; ++i) tmp[i] = filename[i]; // copy filename (basename may modify arg) base_name = basename(tmp); base_name_len = strlen(base_name); static_sym_name = malloc(base_name_len + 1); for (i = 0; i < base_name_len && base_name[i] != '.'; ++i) { if (!is_symbol_char(base_name[i])) { err("found bad character '0x%hhx' in filename \"%s\"\n", base_name[i], filename); free(tmp); return false; } static_sym_name[i] = base_name[i]; } static_sym_name[i] = '\0'; free(tmp); return true; } // translate: iterate over lines in in_file, translate VM instructions to // assembly, write to out_file bool translate(FILE *in_file, FILE *out_file, bool add_init) { char *line, in_line[MAX_LINE_LEN]; struct vm_instruction_t vm_instr; size_t i, line_len; if (add_init) write_vm_init(out_file); for (i = 0; i < MAX_STATIC_SYMBOLS; ++i) static_symbol_map[i] = -1; // initialize symbol map table file_line_no = 0; //instruction_offset = 0; // TODO: unnecessary? while (fgets(in_line, MAX_LINE_LEN, in_file) != NULL) { // parse loop ++file_line_no; file_line = in_line; line = &in_line[skip_whitespace(in_line, MAX_LINE_LEN)]; line_len = 0; for (i = 0; line[i] != '\0'; ++i) { if (line[i] == '\n' || line[i] == '\r') { line[i] = '\0'; // remove newlines break; } ++line_len; // get line length } if (line_len == 0) // "empty" line continue; if (line_len > 1) { if (line[0] == '/' && line[1] == '/') { // if comment continue; } else { vm_instr.line = line; vm_instr.line_len = line_len; if (!parse_line(&vm_instr)) return false; if (!write_instruction(&vm_instr, out_file)) return false; cleanup_vm_instr(&vm_instr); } } } write_vm_stop(out_file); return true; } bool translate_vm_file(char *vm_file_path, FILE *out_file, bool add_init) { FILE *in_file = fopen(vm_file_path, "r"); // read input file if (in_file == NULL) die("failed to open %s for reading\n", vm_file_path); if (!set_static_symbol_name(vm_file_path)) die("error reading file name %s\n", vm_file_path); fprintf(out_file, "// path: %s\n", vm_file_path); // for debugging if (!translate(in_file, out_file, add_init)) // first pass die("failed to translate VM code in file\n"); if (fclose(in_file)) die("failed to close VM file\n"); free(static_sym_name); return true; } char *usage_msg = "Usage: vmtranslator [input/file.vm] [translated/output]\n" " vmtranslator [input/file.vm] # output to STDOUT\n"; int main(int argc, char *argv[]) { bool init; char *fpath; const char *fname, *ext; struct dirent *de; DIR *dr; FILE *out_file; if (argc == 2) { out_file = stdout; } else if (argc == 3) { out_file = fopen(argv[2], "wb"); if (out_file == NULL) die("failed to open %s for writing\n", argv[2]); } else { die("%s", usage_msg); } if (is_directory(argv[1])) { init = true; dr = opendir(argv[1]); if (dr == NULL) die("failed to open directory '%s'\n", argv[1]); while ((de = readdir(dr)) != NULL) { fname = de->d_name; ext = filename_ext(fname); if (ext != NULL) { if (!strcmp("vm", ext)) { fpath = concat_filepath(fname, argv[1]); translate_vm_file(fpath, out_file, init); init = false; free(fpath); } } // else skip file... } } else if (is_regular_file(argv[1])) { translate_vm_file(argv[1], out_file, true); } else { die("error: '%s' isn't a directory or regular file\n", argv[1]); } if (fclose(out_file)) // TODO check if stdout die("failed to close assembly output file\n"); return 0; } |