all repos — nand2tetris @ 549494cd6dd0aef9803e004ce8fffd8443b9c0e7

my nand2tetris progress

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
#include <libgen.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "codewriter.h"
#include "parser.h"
#include "util.h"

#define _DEBUG

char  *file_line;
size_t file_line_no;


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)
{
	char *line, in_line[MAX_LINE_LEN];
	struct vm_instruction_t vm_instr;
	size_t i, line_len;

	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;
}


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;
	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(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 (!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");

	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;
}