git @ Cat's Eye Technologies Kosheri / master src / assemble.c
master

Tree @master (Download .tar.gz)

assemble.c @masterraw · history · blame

/*
 * assemble.c
 * Assembler for virtual machine.
 * $Id: assemble.c 146 2008-12-06 19:59:54Z catseye $
 */

#include "lib.h"
#include "cmdline.h"
#include "value.h"

#include "stream.h"
#include "file.h"

#include "scan.h"
#include "discern.h"

#include "report.h"

#include "gen.h"
#include "instrtab.h"
#include "save.h"

static struct value labels;

/* Routines */

static struct value *
fetch_label(const char *token, unsigned int length)
{
        struct value lab_text;

        value_symbol_new(&lab_text, token, length);
	return value_dict_fetch(&labels, &lab_text);
}

static void
store_label(const char *token, unsigned int length, struct value *label)
{
        struct value lab_text;

        value_symbol_new(&lab_text, token, length);
	value_dict_store(&labels, &lab_text, label);
}

static void
assemble(struct scanner *sc, struct value *gen)
{
	struct opcode_entry *oe;
	struct value label;
	int count;

	while (!scanner_eof(sc)) {
		if (scanner_tokeq(sc, ";")) {	/* A comment - ignore rest of line. */
			scanner_scanline(sc);
			continue;
		}
		if (scanner_tokeq(sc, ":")) {	/* A label - associate string with addr. */
                        const char *str;
                        int len;

			scanner_scan(sc);
                        str = scanner_token_string(sc);
                        len = scanner_token_length(sc);
			value_copy(&label, fetch_label(str, len));
			/* This may cause backpatching */
			if (gen_define_label(gen, &label)) {
				store_label(str, len, &label);
			} else {
				scanner_report(sc, REPORT_ERROR, "Label already defined");
			}
			scanner_scan(sc);
			continue;
		}

		for (oe = opcode_table; oe->token != NULL; oe++) {
			if (scanner_tokeq(sc, oe->token))
				break;
		}
		if (oe->token == NULL) {
			scanner_report(sc, REPORT_ERROR, "Unrecognized token");
			scanner_scan(sc);
			continue;
		}

		gen_integer(gen, oe->opcode);
		scanner_scan(sc);

		for (count = 0; count < oe->arity; count++) {
			if (scanner_tokeq(sc, ":")) {
                                const char *str;
                                int len;

				scanner_scan(sc);           
                                str = scanner_token_string(sc);
                                len = scanner_token_length(sc);
				value_copy(&label, fetch_label(str, len));
				gen_gen_label_ref(gen, &label);
				store_label(str, len, &label);
				scanner_scan(sc);
				continue;
			} else if (scanner_tokeq(sc, "#")) {
				struct value v;

				scanner_scan(sc);
				value_discern(&v, sc);
				gen_value(gen, &v);
				continue;
			} else {
				scanner_report(sc, REPORT_WARNING,
				    "Unrecognized argument");
				scanner_scan(sc);
			}
		}
	}
}

static void
assemble_main(struct value *args, struct value *result)
{
	struct process *out;
	struct scanner *sc;
        struct reporter *r;
	struct value gen, flat; /* the generator that we will use to build vm code */
	struct value *asmfile, *vmfile;
        struct value asmfile_sym, vmfile_sym;

  	r = reporter_new("Assembly", NULL, 1);

        value_symbol_new(&asmfile_sym, "asmfile", 7);
        value_symbol_new(&vmfile_sym, "vmfile", 6);

        assert(value_is_tuple(args));
  	asmfile = value_dict_fetch(args, &asmfile_sym);
	vmfile = value_dict_fetch(args, &vmfile_sym);

	/*
	 * Generate.
	 */
	sc = scanner_new(r);
  
	if (!scanner_open(sc, value_symbol_get_token(asmfile))) {
            value_integer_set(result, 1);
            return;
        }
        
	gen_new_default(&gen);
	value_dict_new(&labels, 8);
	assemble(sc, &gen);
	gen_integer(&gen, INSTR_EOF);
	scanner_close(sc);
	scanner_free(sc);

	/*
	 * Write out.
	 */
	out = file_open(value_symbol_get_token(vmfile), "w");
        gen_flatten(&gen, &flat);
	value_save(out, &flat);
	stream_close(NULL, out);

	value_integer_set(result, reporter_has_errors(r) ? 1 : 0);
        reporter_free(r);
}

MAIN(assemble_main)