#include "pu2.out.c" #include typedef char *string; string read_file(string path, size_t *out_size) { FILE *f = fopen(path, "rb"); string buf; size_t size; if (!f) return NULL; fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); buf = malloc(size + 1); if (!buf) { fclose(f); return NULL; } if (fread(buf, 1, size, f) != size) { free(buf); fclose(f); return NULL; } buf[ size ] = '\0'; fclose(f); if (out_size) *out_size = size; return buf; } error_debug(string fail_point, string code, int sz) { int error_pos = (int) (fail_point - code); int i; int line = 1, col = 1; for (i = 0; i < error_pos && i < sz; i++) { if (code[ i ] == '\n') { line++; col = 1; } else { col++; } } fprintf( stderr, "\x1b[1m\x1b[31merror:\x1b[0m " "\x1b[1massembly failed\x1b[0m " "at \x1b[36mline %d\x1b[0m, \x1b[36mcolumn %d\x1b[0m " "(\x1b[33mposition %d\x1b[0m):\n", line, col, error_pos); int line_start = error_pos; while (line_start > 0 && code[ line_start - 1 ] != '\n') { line_start--; } int line_end = error_pos; while (line_end < sz && code[ line_end ] != '\n' && code[ line_end ] != '\0') { line_end++; } fprintf(stderr, " \x1b[1m"); for (i = line_start; i < line_end; i++) { fputc(code[ i ], stderr); } fprintf(stderr, "\x1b[0m\n"); fprintf(stderr, " "); for (i = line_start; i < error_pos; i++) { fputc((code[ i ] == '\t') ? '\t' : ' ', stderr); } fprintf(stderr, "\x1b[1m\x1b[31m^\x1b[0m \x1b[90mparsing stopped here\x1b[0m\n"); } emu() { while (1) { pu2(); int e_code = m[ 0 ]; if (e_code != 2) return e_code; // handle hardware interrupts int int_code = m[ 1 ]; // 0: output string if (int_code == 0) { int ptr = m[ 2 ]; int size = m[ 3 ], i = 0; while (size--) putchar(m[ ptr++ ]); } // 1: input character else if (int_code == 1) { m[ 2 ] = getchar(); } // 2: arithmetics unit else if (int_code == 2) { int op = m[ 2 ]; int val1 = m[ 3 ]; int val2 = m[ 4 ]; m[ 2 ] = 0; if (op == 0) { // multiply m[ 3 ] = val1 * val2; } else if (op == 1) { // divide m[ 3 ] = val1 / val2; } else if (op == 2) { // modulo m[ 3 ] = val1 % val2; } else if (op == 3) { // left shift m[ 3 ] = val1 << val2; } else if (op == 4) { // right shift m[ 3 ] = val1 >> val2; } else { // unknown op, set m[2] m[ 2 ] = 1; } } else { // unknown interrupt. what to do? } m[ 0 ] = 0; } } skip_space(string *I) { while ((**I == '\n' || isspace(**I)) && (**I != 0)) (*I)++; } // IR parser for pu2's as2 // clang-format off struct { int hlt; int imm; int lod; int str; int swp; int add; int sub; int and; int not; int acc; } opcode = { 0, 1, 2, 3, 4, 5, 6, 7, 8,9 }; // clang-format on typedef unsigned char byte; typedef byte bool; const bool true = 1; const bool false = 0; // use this instead of strdup'ing and allocating extra storage! typedef struct { size_t len; string ref; } slice; typedef struct { slice key; slice value; } entry; clean_slice_ref(slice *val) { free(val->ref); } #define static_len(G) (sizeof(G) / sizeof((G)[ 0 ])) #define emit(OP, VAL) \ ({ \ *Code++ = (OP); \ *Code++ = (VAL); \ }) #define store() \ ({ \ emit(opcode.acc, 1); \ emit(opcode.str, 0); \ }) #define run_err(result) \ ({ \ printf("\n"); \ error_debug((result), ERR_code, strlen(ERR_code)); \ printf("\nir2 error message: %s\n", ERR); \ printf("\n"); \ exit(1); \ }) #define checkskip(char, message) \ ({ \ if (!*I) { \ ERR = (message); \ run_err(I); \ } \ if (*I++ != (char) ) { \ ERR = (message); \ ERR_code = init_I; \ run_err(I - 1); \ } \ }) #define scanword() \ ({ \ slice VAR = { 0, I }; \ while (isalnum(*I) || *I == '_') VAR.len++, I++; \ VAR; \ }) #define memslice slice __attribute__((cleanup(clean_slice_ref))) #define readd() ((used == new.len) ? (new.ref = realloc(new.ref, new.len *= 2)) : 0) #define scanvalue() \ ({ /* value scanner! numbers, characters, strings. */ \ int value; \ if (isdigit(*I)) { \ value = strtol(I, &I, 10); \ } else if (*I == '\'') { \ I++; \ value = *I++; \ checkskip('\'', "couldn't find closing quote on character"); \ } else if (*I == '"') { \ string str_start = ++I; \ bool T = false; \ size_t str_len = 0; \ while (*I != '"' || T) { \ if (*I == '\\') { \ T = !T; \ if (!T && I[ 1 ] == '"') str_len--; \ } \ str_len++; \ I++; \ } \ I++; \ str_len++; \ int *dest_start = Data -= str_len; \ int y; \ for (y = 0; y < str_len - 1; y++) { dest_start[ y ] = str_start[ y ]; } \ Data[ -1 ] = 0; \ value = Data - m; \ } else { \ ERR = "expected value (number, character, string)"; \ run_err(I); \ } \ value; \ }) char T; string ERR; string ERR_code; int *Code = &m[ 4000 ]; int *Data = &m[ static_len(m) - 1 ]; // grows downwards _ir2(string I, entry macros[ 1 << 9 ]) { string init_I = I; ERR = "all good"; while (T = *I) { skip_space(&I); if (T == '/' && I[ 1 ] == '/') { // comment I += 2; while (*I != '\n' && *I != '\0') I++; } // handle assignments! if (T == '@') { I++; int mem_index = strtol(I, &I, 10); skip_space(&I); checkskip('=', "expected assigment after memory blob"); skip_space(&I); if (*I == '@') { I++; if (!isdigit(*I)) { ERR = "expected a digit after @"; run_err(I); } int mem2_index = strtol(I, &I, 10); emit(opcode.imm, mem2_index); emit(opcode.swp, 0); emit(opcode.lod, 0); emit(opcode.swp, 0); emit(opcode.imm, mem_index); store(); } else { int imm = scanvalue(); emit(opcode.imm, imm); emit(opcode.swp, 0); emit(opcode.imm, mem_index); store(); } skip_space(&I); checkskip(';', "expected semicolon"); continue; } // register macros if (strncmp("macro", I, 5) == 0 && isspace(I[ 5 ])) { I += 6; skip_space(&I); // parse macro stuff if (!isalpha(*I) && *I != '_') { ERR = "expected letter at macro name"; run_err(I - 1); } slice name = scanword(); skip_space(&I); checkskip('(', "expected open paren in macro defintion"); skip_space(&I); checkskip( ')', "expected close paren in macro definition (hint: macros do not take named " "arguments)"); skip_space(&I); checkskip('{', "expected open curly bracket in macro body"); // find open space in macros int x; for (x = 0; x < (1 << 9); x++) { entry g = macros[ x ]; if (g.key.ref == NULL) { goto DONE; } } ERR = "macro count exceeded 1<<9"; run_err(I - 1); DONE:; skip_space(&I); // might aswell skip whitespace for funsies. entry *g = ¯os[ x ]; g->key = name; g->value.ref = I; g->value.len = 0; int dep = 0; while (dep != 0 || *I != '}') { if (*I == '{') dep++; if (*I == '}') dep--; g->value.len++; I++; if (*I == 0) { checkskip('}', "expected close curly bracket in macro body (met EOF instead)"); } } I++; continue; } // emit exit calls if (strncmp("exit", I, 4) == 0) { I += 4; checkskip('(', "expected open paren on exit blob"); string old = I; int value = strtol(I, &I, 10); if (old == I && *I != ')') { ERR = "invalid input to exit blob, expected number"; run_err(I - 1); } skip_space(&I); checkskip(')', "expected open paren on exit blob"); skip_space(&I); checkskip(';', "expected semicolon after call"); // actually emit stuff if (old != I) emit(opcode.imm, value); emit(opcode.hlt, 0); continue; } // emit system if (strncmp("system", I, 6) == 0 && !isalnum(I[ 7 ])) { I += 6; skip_space(&I); checkskip('(', "expected open paren on system blob"); skip_space(&I); checkskip( ')', "expected closing paren on system blob (hint: system does not take any arguments)"); skip_space(&I); checkskip(';', "expected semicolon after system blob"); emit(opcode.imm, 2); emit(opcode.swp, 0); emit(opcode.imm, 0); store(); emit(opcode.hlt, 0); continue; } // macro call, maybe? if (isalpha(*I)) { slice name = scanword(); skip_space(&I); checkskip('(', "expected open paren after alphanumeric word (assuming this is a blob)"); skip_space(&I); slice arguments[ 1 << 7 ]; size_t arg_count = 0; while (*I != ')') { if (!*I) { ERR = "reached EOF whilst looking for closing paren on blob"; run_err(I); } checkskip('[', "expected bracket in blob argument (macro-type)"); arguments[ arg_count ].len = 0; arguments[ arg_count ].ref = I; int dep = 0; while (dep != 0 || *I != ']') { if (*I == '[') dep++; if (*I == ']') dep--; arguments[ arg_count ].len++; I++; if (*I == 0) { checkskip( ']', "expected closing bracket in macro argument (met EOF instead)"); } } skip_space(&I); checkskip(']', "expected closing bracket in macro arguments"); skip_space(&I); if (*I != ',' && *I != ')') { ERR = "expected comma or closing paren in blob"; run_err(I); } if (*I == ',') { I++; skip_space(&I); } arg_count++; } checkskip(')', "expected closing paren after blob arguments"); skip_space(&I); checkskip(';', "expected semicolon after blob"); // done reading blob! check it and parse macro :) // find index size_t i; for (i = 0; i < (1 << 9); i++) { entry cur = macros[ i ]; if (cur.key.len != name.len) continue; if (strncmp(name.ref, cur.key.ref, name.len) == 0) goto FOUND; } ERR = "could not find a macro by that name"; run_err(I); FOUND:; slice target = macros[ i ].value; string N = target.ref; string oldN = N; memslice new = { target.len, malloc(target.len) }; size_t used = 0; bool needs_scan = true; while (needs_scan) { needs_scan = false; while (*N) { if (N - oldN >= target.len) break; if (*N == '/' && isdigit(N[ 1 ])) { N++; string old = N - 1; size_t index = strtol(N, &N, 10); if (index > arg_count) { N--; goto END; } if (*N++ != '/') { N = old + 1; while (*N != '/') { new.ref[ used++ ] = *N++; readd(); } goto END; } needs_scan = true; slice rep = arguments[ index - 1 ]; string val = rep.ref; for (i = 0; i < rep.len; i++) { new.ref[ used++ ] = *val++; readd(); } continue; } END: new.ref[ used++ ] = *N++; readd(); } } new.ref[ used ] = 0; _ir2(new.ref, macros); continue; } } } ir2(string I) { entry macros[ 1 << 9 ]; _ir2(I, macros); } string op_names[] = { "hlt", "imm", "lod", "str", "swp", "add", "sub", "and", "not", "acc" }; main() { size_t o_size; ir2(read_file("code.i2", &o_size)); m[ 7 ] = 4000; size_t res_size = Code - &m[ 4000 ]; // --- BYTES PRINTING --- // for(i = 0; i < res_size; i++) { // printf("%X%X ", m[4000+i]>>4, m[4000+i]&0b1111); // if(i % 8 == 7) printf("\n"); // } // fflush(stdout); // printf("\n\n"); // --- OPCODES PRINTING --- // for (i = 0; i < res_size; i++) { // int op = m[ 4000 + i++ ]; // int data = m[ 4000 + i ]; // printf("%s(%i);\n", op_names[ op ], data); // } // printf("\n\n"); emu(); } main2(int argc, char **argv) { if (argc == 1) { return printf( "\nusage:\n" " %s \n\n", argv[ 0 ]); } char *filename = argv[ 1 ]; size_t file_size; char *contents = read_file(filename, &file_size); E = 0; K = &m[ 1024 ]; as2(contents); if (E != 0) { error_debug(E, contents, file_size); exit(1); } m[ 7 ] = 1024; emu(); }