Last active
April 16, 2017 06:30
-
-
Save dxsmiley/35416396d2cde813fa43b2bc5e85b5a8 to your computer and use it in GitHub Desktop.
Befunge interpreter in one c file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Befunge interpereter, in one large file. | |
| Code taken from this repo: https://github.com/programble/befungeec | |
| It's been smooshed into one file and modified somewhat. | |
| Improvements: | |
| - Shows a special symbols for unprintable characters in debug mode. | |
| - Shows a help message when run with no arguments, rather than crashing. | |
| - Found `atoi(&c)` being used to convert a single `char` to an integer. Never again. | |
| - More compact debugger output: lines are trimmed and extranious lines are not printed. | |
| - Compile flag (-DLARGE_BOARD) to change board size. | |
| - Protections against get / set of out-of-bounds board indexes. | |
| */ | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <time.h> | |
| #include <string.h> | |
| /* | |
| * stack.h | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| typedef struct stack_item | |
| { | |
| struct stack_item *next; | |
| char value; | |
| } stack_item; | |
| typedef struct stack | |
| { | |
| stack_item *top; | |
| } stack; | |
| stack *stack_new(); | |
| void stack_push(stack*, char); | |
| char stack_pop(stack*); | |
| char stack_peek(stack*); | |
| int stack_length(stack*); | |
| int stack_empty(stack*); | |
| /* | |
| * pointer.h | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| typedef struct pointer | |
| { | |
| int x; | |
| int y; | |
| int dx; | |
| int dy; | |
| stack *stack; | |
| int strmode; | |
| } pointer; | |
| pointer *pointer_new(); | |
| void pointer_move(pointer*); | |
| /* | |
| * board.h | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| #ifdef LARGE_BOARD | |
| #define BOARD_WIDTH 100 | |
| #define BOARD_HEIGHT 100 | |
| #else | |
| #define BOARD_WIDTH 30 | |
| #define BOARD_HEIGHT 30 | |
| #endif | |
| typedef struct board | |
| { | |
| char data[BOARD_HEIGHT][BOARD_WIDTH]; | |
| pointer *pointer; | |
| } board; | |
| board *board_new(); | |
| char board_get(board*, int, int); | |
| void board_put(board*, int, int, char); | |
| /* | |
| * stack.c | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| stack *stack_new() | |
| { | |
| stack *s = malloc(sizeof(stack)); | |
| s->top = NULL; | |
| return s; | |
| } | |
| void stack_push(stack *s, char value) | |
| { | |
| stack_item *item = malloc(sizeof(stack_item)); | |
| if (s->top) | |
| { | |
| item->value = value; | |
| item->next = s->top; | |
| s->top = item; | |
| } | |
| else | |
| { | |
| item->value = value; | |
| item->next = NULL; | |
| s->top = item; | |
| } | |
| } | |
| char stack_pop(stack *s) | |
| { | |
| if (s->top) | |
| { | |
| stack_item *item = s->top; | |
| s->top = item->next; | |
| char value = item->value; | |
| free(item); | |
| return value; | |
| } | |
| else | |
| { | |
| return 0x0; | |
| } | |
| } | |
| char stack_peek(stack *s) | |
| { | |
| if (s->top) | |
| { | |
| stack_item *item = s->top; | |
| return item->value; | |
| } | |
| else | |
| { | |
| return 0x0; | |
| } | |
| } | |
| int stack_length(stack *s) | |
| { | |
| stack_item *item = s->top; | |
| int i = 0; | |
| while (item) | |
| { | |
| i++; | |
| item = item->next; | |
| } | |
| return i; | |
| } | |
| int stack_empty(stack *s) | |
| { | |
| return (!s->top); | |
| } | |
| /* | |
| * pointer.c | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| pointer *pointer_new() | |
| { | |
| pointer *p = malloc(sizeof(pointer)); | |
| p->x = 0; | |
| p->y = 0; | |
| p->dx = 1; | |
| p->dy = 0; | |
| p->stack = stack_new(); | |
| p->strmode = 0; | |
| return p; | |
| } | |
| void pointer_move(pointer* p) | |
| { | |
| p->x += p->dx; | |
| p->y += p->dy; | |
| } | |
| /* | |
| * board.c | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| board *board_new() | |
| { | |
| board *b = malloc(sizeof(board)); | |
| b->pointer = pointer_new(); | |
| int y, x; | |
| for (y = 0; y < BOARD_HEIGHT; y++) | |
| { | |
| for (x = 0; x < BOARD_WIDTH; x++) | |
| { | |
| b->data[y][x] = ' '; | |
| } | |
| } | |
| return b; | |
| } | |
| char board_get(board *b, int x, int y) | |
| { | |
| if (x < 0 || y < 0 || x >= BOARD_WIDTH || y >= BOARD_HEIGHT) return 0; | |
| return b->data[y][x]; | |
| } | |
| void board_put(board *b, int x, int y, char value) | |
| { | |
| if (x >= 0 && y >= 0 && x < BOARD_WIDTH && y < BOARD_HEIGHT) b->data[y][x] = value; | |
| } | |
| /* | |
| * main.c | |
| * | |
| * Copyright 2010 Curtis (Programble) <programble@gmail.com> | |
| * Licensed under the GNU GPLv3 | |
| */ | |
| int debugflag = 0; | |
| FILE *debugstream; | |
| void debug(board *board) | |
| { | |
| int height = 0; | |
| for (int y = 0; y < BOARD_HEIGHT; y++) | |
| { | |
| for (int x = 0; x < BOARD_WIDTH; ++x) { | |
| if (board_get(board, x, y) != ' ') { | |
| height = y + 1; | |
| } | |
| } | |
| } | |
| printf("\x1b[H\x1b[2J"); | |
| char direction; | |
| if (board -> pointer -> dx == 1) | |
| direction = '>'; | |
| else if (board -> pointer -> dx == -1) | |
| direction = '<'; | |
| else if (board -> pointer -> dy == 1) | |
| direction = 'v'; | |
| else if (board -> pointer -> dy == -1) | |
| direction = '^'; | |
| printf("Pointer: %d,%d %c %s\n", board->pointer->x, board->pointer->y, direction, (board->pointer->strmode)?"string mode":""); | |
| printf("Board:\n"); | |
| int y, x; | |
| for (y = 0; y < height; y++) | |
| { | |
| int width = 0; | |
| for (x = 0; x < BOARD_WIDTH; ++x) { | |
| if (board_get(board, x, y) != ' ') width = x + 1; | |
| } | |
| for (x = 0; x < width; x++) | |
| { | |
| int value = board_get(board, x, y); | |
| if (x == board->pointer->x && y == board->pointer->y) { | |
| if (value < 32 || value > 126) { | |
| printf("\033[41m\033[35m?\033[0m"); | |
| // printf("\033[41m%c\033[0m", value); | |
| } else { | |
| printf("\033[41m%c\033[0m", value); | |
| } | |
| } else { | |
| // If it's an unprintable character, show it. | |
| if (value < 32 || value > 126) { | |
| // printf("?"); | |
| printf("\033[35m?\033[0m"); | |
| } else { | |
| printf("%c", value); | |
| } | |
| } | |
| // printf("%s%c\033[0m", (x == board->pointer->x && y == board->pointer->y)?"\033[41m":"", board_get(board, x, y)); | |
| } | |
| printf("\n"); | |
| } | |
| printf("Stack:\n"); | |
| stack_item *item = board->pointer->stack->top; | |
| while (item) | |
| { | |
| switch (item -> value) { | |
| case '\0': | |
| printf("(0 \033[35mnull\033[0m) "); break; | |
| case '\n': | |
| printf("(10 \033[35mnewline\033[0m) "); break; | |
| default: | |
| if (item -> value >= 32 && item -> value <= 126) { | |
| printf("(%d '%c') ", item -> value, item -> value); break; | |
| } else { | |
| printf("(%d \033[35munknown\033[0m) ", item -> value); break; | |
| } | |
| } | |
| item = item->next; | |
| } | |
| printf("\n"); | |
| printf("Output:\n"); | |
| fseek(debugstream, 0, SEEK_SET); | |
| char c; | |
| while ((c = fgetc(debugstream)) != EOF) | |
| { | |
| printf("%c", c); | |
| } | |
| getchar(); | |
| } | |
| void process(board *board) | |
| { | |
| FILE *out; | |
| if (debugflag) | |
| out = debugstream; | |
| else | |
| out = stdout; | |
| while (1) | |
| { | |
| char c = board_get(board, board->pointer->x, board->pointer->y); | |
| if (board->pointer->strmode && c != '"') | |
| { | |
| stack_push(board->pointer->stack, c); | |
| } | |
| else | |
| { | |
| switch (c) | |
| { | |
| case '0': | |
| case '1': | |
| case '2': | |
| case '3': | |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| stack_push(board->pointer->stack, c - '0'); | |
| break; | |
| case '+': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, a + b); | |
| break; | |
| } | |
| case '-': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, b - a); | |
| break; | |
| } | |
| case '*': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, a * b); | |
| break; | |
| } | |
| case '/': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, b / a); | |
| break; | |
| } | |
| case '%': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, b % a); | |
| break; | |
| } | |
| case '!': | |
| stack_push(board->pointer->stack, !stack_pop(board->pointer->stack)); | |
| break; | |
| case '`': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, (b > a)); | |
| break; | |
| } | |
| case '>': | |
| board->pointer->dx = 1; | |
| board->pointer->dy = 0; | |
| break; | |
| case '<': | |
| board->pointer->dx = -1; | |
| board->pointer->dy = 0; | |
| break; | |
| case '^': | |
| board->pointer->dx = 0; | |
| board->pointer->dy = -1; | |
| break; | |
| case 'v': | |
| board->pointer->dx = 0; | |
| board->pointer->dy = 1; | |
| break; | |
| case '?': | |
| { | |
| srand((unsigned)time(NULL)); | |
| int r = rand() % 4; | |
| switch (r) | |
| { | |
| case 0: | |
| board->pointer->dx = 1; | |
| board->pointer->dy = 0; | |
| break; | |
| case 1: | |
| board->pointer->dx = -1; | |
| board->pointer->dy = 0; | |
| break; | |
| case 2: | |
| board->pointer->dx = 0; | |
| board->pointer->dy = -1; | |
| break; | |
| case 3: | |
| board->pointer->dx = 0; | |
| board->pointer->dy = 1; | |
| break; | |
| } | |
| break; | |
| } | |
| case '_': | |
| { | |
| char x = stack_pop(board->pointer->stack); | |
| if (x == 0) | |
| { | |
| board->pointer->dx = 1; | |
| board->pointer->dy = 0; | |
| } | |
| else | |
| { | |
| board->pointer->dx = -1; | |
| board->pointer->dy = 0; | |
| } | |
| break; | |
| } | |
| case '|': | |
| { | |
| char x = stack_pop(board->pointer->stack); | |
| if (x == 0) | |
| { | |
| board->pointer->dx = 0; | |
| board->pointer->dy = 1; | |
| } | |
| else | |
| { | |
| board->pointer->dx = 0; | |
| board->pointer->dy = -1; | |
| } | |
| break; | |
| } | |
| case '"': | |
| board->pointer->strmode = !board->pointer->strmode; | |
| break; | |
| case ':': | |
| stack_push(board->pointer->stack, stack_peek(board->pointer->stack)); | |
| break; | |
| case '\\': | |
| { | |
| char a = stack_pop(board->pointer->stack); | |
| char b = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, a); | |
| stack_push(board->pointer->stack, b); | |
| break; | |
| } | |
| case '$': | |
| stack_pop(board->pointer->stack); | |
| break; | |
| case '.': | |
| fprintf(out, "%d ", (unsigned char)stack_pop(board->pointer->stack)); | |
| break; | |
| case ',': | |
| fprintf(out, "%c", stack_pop(board->pointer->stack)); | |
| break; | |
| case '#': | |
| pointer_move(board->pointer); | |
| break; | |
| case 'p': | |
| { | |
| int y = stack_pop(board->pointer->stack); | |
| int x = stack_pop(board->pointer->stack); | |
| char v = stack_pop(board->pointer->stack); | |
| board_put(board, x, y, v); | |
| break; | |
| } | |
| case 'g': | |
| { | |
| int y = stack_pop(board->pointer->stack); | |
| int x = stack_pop(board->pointer->stack); | |
| stack_push(board->pointer->stack, board_get(board, x, y)); | |
| break; | |
| } | |
| case '&': | |
| { | |
| int i; | |
| scanf(" %d", &i); | |
| stack_push(board->pointer->stack, (char) i); | |
| break; | |
| } | |
| case '~': | |
| stack_push(board->pointer->stack, getchar()); | |
| break; | |
| case '@': | |
| return; | |
| } | |
| } | |
| pointer_move(board->pointer); | |
| if (board->pointer->x < 0) | |
| { | |
| board->pointer->x += BOARD_WIDTH; | |
| } | |
| if (board->pointer->x >= BOARD_WIDTH) | |
| { | |
| board->pointer->x -= BOARD_WIDTH; | |
| } | |
| if (board->pointer->y < 0) | |
| { | |
| board->pointer->y += BOARD_HEIGHT; | |
| } | |
| if (board->pointer->y >= BOARD_HEIGHT) | |
| { | |
| board->pointer->y -= BOARD_HEIGHT; | |
| } | |
| if (debugflag && board_get(board, board -> pointer -> x, board -> pointer -> y) != ' ') | |
| { | |
| debug(board); | |
| } | |
| } | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| if (argc == 1) { | |
| printf("Usage: befunge [--debug] source\n"); | |
| return 0; | |
| } | |
| board *board = board_new(); | |
| FILE *f; | |
| if (argc < 2 || strcmp(argv[argc-1], "--") == 0) | |
| { | |
| f = stdin; | |
| } | |
| else | |
| { | |
| f = fopen(argv[argc-1], "r"); | |
| if (f == NULL) | |
| { | |
| perror("open"); | |
| return 1; | |
| } | |
| } | |
| if (strcmp(argv[1], "-d") == 0 || strcmp(argv[1], "--debug") == 0) | |
| debugflag = 1; | |
| char c; | |
| int x = 0; | |
| int y = 0; | |
| while ((c = fgetc(f)) != EOF) | |
| { | |
| if (c == '\n') | |
| { | |
| y++; | |
| x = 0; | |
| continue; | |
| } | |
| else | |
| { | |
| board_put(board, x, y, c); | |
| } | |
| if (x > BOARD_WIDTH || y > BOARD_HEIGHT) | |
| { | |
| fprintf(stderr, "Error: File too large (%i, %i)", x, y); | |
| return 1; | |
| } | |
| x++; | |
| } | |
| if (f != stdin) | |
| { | |
| fclose(f); | |
| } | |
| if (debugflag) | |
| { | |
| debugstream = tmpfile(); | |
| } | |
| process(board); | |
| if (debugflag) | |
| { | |
| fclose(debugstream); | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment