all repos — nand2tetris @ 6b59b2d1aa8d1c2c01d77b7a7d383be5fcfb4adc

my nand2tetris progress

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

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

#define _DEBUG

#ifdef _DEBUG
#define DBGLOG   printf
#else
#define DBGLOG(...)
#endif

#define err(...) (fprintf(stderr, __VA_ARGS__), \
                  fprintf(stderr, "%lu | %s\n", file_line_no, file_line), false)
#define die(...)   do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)

#define is_symbol_char(c) (('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') \
                        || ('a' <= c && c <= 'z') || c == '_' || c == '.'   \
                                                  || c == '$' || c == ':')
#define is_whitespace(c) ((c == ' ' || c == '\t' || c == '\n' || c == '\r'))
#define is_number(c) (('0' <= c && c <= '9'))

#define MAX_LINE_LEN          256 // TODO: in/excludes NULL terminator?
#define MAX_SYMBOL_LEN        MAX_LINE_LEN - 2 + 1 // + 1 for NULL terminator

char  *file_line;     // reference to currently-read line (for convenience)
size_t file_line_no;  // line number, regardless of line content


static size_t skip_whitespace(const char *line, size_t n)
{
	size_t i;
	for (i = 0; is_whitespace(line[i]) && i < n; ++i);
	return i;
}

bool translate(FILE *in_file)
{
	char *line, in_line[MAX_LINE_LEN], *translated;
	struct vm_instruction_t vm_instr;
	size_t i, line_len;

	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 {
				// parser will error specifically (TODO remove me)
				if (!parse_line(line, &vm_instr))
					return false;
				if (!write_instruction(&vm_instr, in_file))
					return false;
			}
		}
	}

	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(!translate(in_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))
		die("failed to close assembly output file\n");

	return 0;
}