#include "vm/vm.h" #include "vm/print.h" #include "parse/parse.h" #include "parse/lex.h" #include "io.h" #include "bitset.h" #include "bytecode.h" #include #include static int do_print_bytecode = 0; static int do_print_tokens = 0; static int do_step = 0; static int do_serialize_bytecode = 0; static char *input_filename = "-"; static int serialize_bytecode(FILE *outf, l2_word *data, size_t len) { char header[4] = { 0x1b, 0x6c, 0x32, 0x63 }; if (fwrite(header, 1, 4, outf) < 4) { fprintf(stderr, "Write error\n"); return -1; } char version[4] = { (l2_bytecode_version & 0xff000000ul) >> 24, (l2_bytecode_version & 0x00ff0000ul) >> 16, (l2_bytecode_version & 0x0000ff00ul) >> 8, (l2_bytecode_version & 0x000000fful) >> 0, }; if (fwrite(version, 1, 4, outf) < 4) { fprintf(stderr, "Write error\n"); return -1; } for (size_t i = 0; i < len; ++i) { l2_word word = data[i]; char *dest = (char *)&data[i]; dest[0] = (word & 0xff000000ul) >> 24; dest[1] = (word & 0x00ff0000ul) >> 16; dest[2] = (word & 0x0000ff00ul) >> 8; dest[3] = (word & 0x000000fful) >> 0; } if (fwrite(data, 4, len, outf) < len) { fprintf(stderr, "Write error\n"); return -1; } return 0; } static int load_bytecode(FILE *inf, struct l2_io_writer *w) { // Header is already read by main char version_buf[4]; if (fread(version_buf, 1, 4, inf) < 4) { fprintf(stderr, "Read error\n"); return -1; } int version = 0 | ((unsigned int)version_buf[0]) << 24 | ((unsigned int)version_buf[1]) << 16 | ((unsigned int)version_buf[2]) << 8 | ((unsigned int)version_buf[3]) << 0; if (version != l2_bytecode_version) { fprintf( stderr, "Version mismatch! Bytecode file uses bytecode version %i" ", but your build of lang2 uses bytecode version %i\n", version, l2_bytecode_version); return -1; } struct l2_bufio_writer writer; l2_bufio_writer_init(&writer, w); char buffer[4096]; while (1) { size_t n = fread(buffer, 1, sizeof(buffer), inf); if (n < 4) { l2_bufio_flush(&writer); return 0; } for (size_t i = 0; i < n; i += 4) { l2_word word = 0 | ((unsigned int)buffer[i + 0]) << 24 | ((unsigned int)buffer[i + 1]) << 16 | ((unsigned int)buffer[i + 2]) << 8 | ((unsigned int)buffer[i + 3]) << 0; l2_bufio_put_n(&writer, &word, 4); } } } static int parse_text(FILE *inf, struct l2_io_writer *w) { // Init lexer with its input reader struct l2_io_file_reader r; r.r.read = l2_io_file_read; r.f = inf; struct l2_lexer lexer; l2_lexer_init(&lexer, &r.r); if (do_print_tokens) { lexer.do_log_tokens = 1; } // Init gen with its output writer struct l2_generator gen; l2_gen_init(&gen, w); struct l2_parse_error err; if (l2_parse_program(&lexer, &gen, &err) < 0) { fprintf(stderr, "Parse error: %s:%i:%i: %s\n", input_filename, err.line, err.ch, err.message); l2_gen_free(&gen); fclose(inf); return -1; } l2_gen_free(&gen); return 0; } static void step_through(struct l2_vm *vm) { printf("=====\n\nInitial state:\n"); l2_vm_print_state(vm); char buf[16]; while (!vm->halted) { size_t iptr = vm->iptr; printf("\n======\n\n(%d) Will run instr: ", vm->iptr); l2_vm_print_op(vm->ops, vm->opcount, &iptr); if (fgets(buf, sizeof(buf), stdin) == NULL) { break; } l2_vm_step(vm); l2_vm_print_state(vm); } } static void usage(const char *argv0) { printf("Usage: %s [options] [input|-]\n", argv0); printf("\n"); printf("Options:\n"); printf(" --help: Print this help text\n"); printf(" --bytecode: Print the generated bytecode, don't execute\n"); printf(" --tokens: Print the tokens as the program is parsed, don't execute\n"); printf(" --step: Step through the program\n"); printf(" --output,-o : Write bytecode to file\n"); } int main(int argc, char **argv) { int was_inf_set = 0; FILE *inf = stdin; FILE *outbc = NULL; int dashes = 0; for (int i = 1; i < argc; ++i) { if (!dashes && strcmp(argv[i], "--help") == 0) { usage(argv[0]); return 0; } else if (!dashes && strcmp(argv[i], "--bytecode") == 0) { do_print_bytecode = 1; } else if (!dashes && strcmp(argv[i], "--tokens") == 0) { do_print_tokens = 1; } else if (!dashes && strcmp(argv[i], "--step") == 0) { do_step = 1; } else if (!dashes && ( strcmp(argv[i], "--output") == 0 || strcmp(argv[i], "-o") == 0)) { if (i == argc - 1) { fprintf(stderr, "%s expects an argument\n", argv[i]); return 1; } do_serialize_bytecode = 1; i += 1; if (strcmp(argv[i], "-") == 0) { outbc = stdout; } else { outbc = fopen(argv[i], "w"); if (outbc == NULL) { perror(argv[i]); return 1; } } } else if (strcmp(argv[i], "--") == 0) { dashes = 1; } else if (strcmp(argv[i], "-") == 0) { input_filename = "-"; inf = stdin; } else if (!was_inf_set) { input_filename = argv[i]; inf = fopen(argv[i], "r"); was_inf_set = 1; if (inf == NULL) { perror(argv[i]); return 1; } } else { printf("Unexpected argument: %s\n", argv[i]); usage(argv[0]); return 1; } } struct l2_io_mem_writer bytecode_writer = {0}; bytecode_writer.w.write = l2_io_mem_write; // int headerbyte = fgetc(inf); if (headerbyte == EOF || ungetc(headerbyte, inf) == EOF) { fprintf(stderr, "%s: Reading file failed.\n", input_filename); return 1; } // Detect whether input is compiled bytecode or not // (compile bytecode starts with (ESC) 'l' '2' 'c') unsigned char header[4]; if ( headerbyte == 0x1b && fread(header, 1, 4, inf) >= 4 && header[0] == 0x1b && header[1] == 0x6c && header[2] == 0x32 && header[3] == 0x63) { if (load_bytecode(inf, &bytecode_writer.w) < 0) { return 1; } } else if (headerbyte == 0x1b) { fprintf( stderr, "%s: Corrupt file? Start byte is 0x1b (ESC)" "but the header is wrong\n", input_filename); return 1; } else { if (parse_text(inf, &bytecode_writer.w) < 0) { return 1; } } fclose(inf); if (do_print_bytecode) { l2_vm_print_bytecode(bytecode_writer.mem, bytecode_writer.len / sizeof(l2_word)); } if (do_serialize_bytecode) { serialize_bytecode(outbc, bytecode_writer.mem, bytecode_writer.len / sizeof(l2_word)); } if (do_print_bytecode || do_print_tokens || do_serialize_bytecode) { free(bytecode_writer.mem); return 0; } struct l2_vm vm; l2_vm_init(&vm, bytecode_writer.mem, bytecode_writer.len / sizeof(l2_word)); if (do_step) { step_through(&vm); } else { l2_vm_run(&vm); } l2_vm_free(&vm); free(bytecode_writer.mem); }