all repos — nand2tetris @ 231ae71af5c290e14ade0a7858c31f5c28743b1a

my nand2tetris progress

Handle directory arg, translate inner VM files
x1phosura x1phosura@x1phosura.zone
Sun, 26 Nov 2023 20:10:47 -0800
commit

231ae71af5c290e14ade0a7858c31f5c28743b1a

parent

67290b1e3bef8ebc7c9e28b03c7adc54a54c74a2

2 files changed, 127 insertions(+), 15 deletions(-)

jump to
M projects/08/src/codewriter.hprojects/08/src/codewriter.h

@@ -362,18 +362,45 @@ }

static bool write_function(struct vm_instruction_t *vm_instr, FILE *fp) { + /* + * (functionName) // injects function entry label into the code + * repeat nVars times: // nVars = number of local variables + * push 0 // initializes local variable to 0 + */ print_vm_instruction(vm_instr, fp); return true; // STUB TODO implement } static bool write_return(struct vm_instruction_t *vm_instr, FILE *fp) { + /* + * frame = LCL // frame is a temporary variable + * retAddr = *(frame - 5) // put return address in a temporary variable + * *ARG = pop() // repositions the return value for the caller + * SP = ARG+1 // repositions SP for the caller + * THAT = *(frame - 1) // restores THAT for the caller + * THIS = *(frame - 2) // restores THIS for the caller + * ARG = *(frame - 3) // restores ARG for the caller + * LCL = *(frame - 4) // restores LCL for the caller + * goto retAddr // jump to where return address points + */ print_vm_instruction(vm_instr, fp); return true; // STUB TODO implement } static bool write_call(struct vm_instruction_t *vm_instr, FILE *fp) { + /* + * push retAddr // generates a label and pushes it to the stack + * push LCL // saves LCL of caller + * push ARG // saves ARG of caller + * push THIS // saves THIS of caller + * push THAT // saves THAT of caller + * ARG = SP - 5 - nArgs // repositions ARG + * LCL = SP // repositions LCL + * goto functionName // transfers control to the callee + * (retAddr) // injects the return address label into the code + */ print_vm_instruction(vm_instr, fp); return true; // STUB TODO implement }
M projects/08/src/vmtranslator.cprojects/08/src/vmtranslator.c

@@ -1,9 +1,13 @@

+#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"

@@ -11,11 +15,55 @@ #include "util.h"

#define _DEBUG +#define MAX_FILE_PATH_LEN (4096) + char *file_line; size_t file_line_no; -bool set_static_symbol_name(char *filename) { +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 +static bool set_static_symbol_name(char *filename) { size_t i, filename_len, base_name_len; char *tmp, *base_name;

@@ -44,13 +92,14 @@ }

// 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 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; - write_vm_init(out_file); + 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

@@ -91,13 +140,37 @@ 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[]) { - FILE *in_file, *out_file; + 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) {

@@ -105,23 +178,35 @@ out_file = fopen(argv[2], "wb");

if (out_file == NULL) die("failed to open %s for writing\n", argv[2]); } else { - die(usage_msg); + die("%s", usage_msg); } - in_file = fopen(argv[1], "r"); // read input file - if (in_file == NULL) - die("failed to open %s for reading\n", argv[1]); + if (is_directory(argv[1])) { + init = true; + dr = opendir(argv[1]); + if (dr == NULL) + die("failed to open directory '%s'\n", argv[1]); - if (!set_static_symbol_name(argv[1])) - die("error reading file name %s\n", argv[1]); - if (!translate(in_file, out_file)) // first pass - die("failed to translate VM code in file\n"); + 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(in_file)) - die("failed to close VM file\n"); if (fclose(out_file)) // TODO check if stdout die("failed to close assembly output file\n"); - free(static_sym_name); return 0; }