#include #include #include #include #include "program.h" #include "intmap.h" /* ==================== Static Data ==================== */ THREAD_FUNC run_thread(ThreadArg arg) { Subprocess proc = *((Subprocess *) arg); proc.counter = 0; proc.pointer = 0; while (proc.counter < proc.code_size && *(proc.running)) { char c = proc.code[proc.counter], buf; int result; switch (c) { /* Regular BF actions follow */ case '>': proc.pointer++; break; case '<': proc.pointer--; break; case '+': proc.memory[proc.pointer]++; break; case '-': proc.memory[proc.pointer]--; break; case '.': putchar(proc.memory[proc.pointer]); fflush(stdout); break; case ',': proc.memory[proc.pointer] = getch(); break; case '[': { /* We need only to look up the companion bracket position from our pre-built mapping. */ if (proc.memory[proc.pointer] == 0) { intmap_get(proc.brackets, proc.counter, &proc.counter); } break; } case ']': { /* Our counter will need to be one behind, so the incrementing at the loop end won't mess us up. */ if (proc.memory[proc.pointer] != 0) { intmap_get(proc.brackets, proc.counter, &proc.counter); proc.counter--; } break; } case '^': /* Write the current cell value to the socket, if possible */ if (*proc.sock == INVALID_SOCKET) { fprintf(stderr, "Attempted send with no socket\n"); goto terminate; } buf = (char) proc.memory[proc.pointer]; result = socket_write(proc.sock, &buf, 1); if (result <= 0) { fprintf(stderr, "Could not write to socket\n"); goto terminate; } break; case 'v': /* Set the current cell value to the byte from the socket */ if (*proc.sock == INVALID_SOCKET) { fprintf(stderr, "Attemped recv with no socket\n"); goto terminate; } result = socket_read(proc.sock, &buf, 1); if (result <= 0) { fprintf(stderr, "Could not read from socket\n"); goto terminate; } proc.memory[proc.pointer] = (uint8_t) buf; break; case '*': /* Make a cell-value-dependent sound */ emit_beep(proc.memory[proc.pointer]); break; case '~': /* Wait the number cell-value number of milliseconds */ sleep_ms(proc.memory[proc.pointer] * 10); break; case '&': /* Unconditionally halt the termination of the program */ *(proc.running) = 0; goto terminate; break; } proc.counter++; } terminate: END_THREAD(); } void subprocess_init(Subprocess *proc, Program *program, int num) { assert(program); assert(num == 0 || num == 1); proc->memory = program->memory; proc->running = &program->running; proc->code = program->codes[num]; proc->code_size = program->code_sizes[num]; proc->brackets = &program->brackets; proc->sock = &program->sock; } /* ==================== Extern Data ==================== */ Program *program_create(const char *source) { Program *program; uint32_t idx, *stack, next, capacity; assert(source); program = malloc(sizeof(Program)); memset(program, 0, sizeof(Program)); intmap_init(&program->brackets); /* Record the offsets of matching brackets, and check for a possible thread divider in the source. */ stack = malloc(sizeof(uint32_t) * 64); for (idx = 0, next = 0, capacity = 64; source[idx] != 0; idx++) { if (source[idx] == '[') { /* Ensure we grow the stack as needed */ if (next >= capacity) { capacity *= 2; stack = realloc(stack, sizeof(uint32_t) * capacity); } stack[next++] = idx; } else if (source[idx] == ']') { uint32_t opener; /* There must be an opening bracket to match this closing one */ if (next == 0) { free(program); free(stack); return NULL; } opener = stack[--next]; intmap_put(&program->brackets, idx, opener); intmap_put(&program->brackets, opener, idx); printf("Linking %u <=> %u\n", idx, opener); } else if (source[idx] == '|') { /* Check that this is the first pipe found; only one is allowed! */ if (program->codes[1] != NULL) { free(program); free(stack); return NULL; } program->codes[1] = source + idx + 1; program->code_sizes[1] = strlen(program->codes[1]); } } free(stack); program->codes[0] = source; program->code_sizes[0] = idx; program->sock = INVALID_SOCKET; return program; } void program_destroy(Program *program) { assert(program); if (program->sock != INVALID_SOCKET) socket_close(&program->sock); intmap_cleanup(&program->brackets); free(program); } int program_connect(Program *program, const char *hostname, const char *port) { Socket server; int result; assert(program); assert(port); if (socket_open(&program->sock, hostname, port) == 0) return 0; if (socket_server(&server, port) == 0) { result = socket_accept(&server, &program->sock); socket_close(&server); } else result = -1; return result; } int program_execute(Program *program) { Subprocess procs[2]; assert(program); program->running = 1; subprocess_init(&procs[0], program, 0); thread_create(&program->threads[0], run_thread, &procs[0]); if (program->codes[1] != NULL) { subprocess_init(&procs[1], program, 1); thread_create(&program->threads[1], run_thread, &procs[1]); } thread_join(&program->threads[0]); if (program->codes[1] != NULL) thread_join(&program->threads[1]); return 0; }