OBFI

Brainfuck Interpreter
git clone http://git.omkov.net/OBFI
Log | Tree | Refs | README | LICENCE | Download

OBFI/src/obfi.c (164 lines, 4.9 KiB) -rw-r--r-- file download

1dbad8b Jakob Wakeling 2020-07-14 13:19:48
0
// Copyright (C) 2020, Jakob Wakeling
5a79c59 Jakob Wakeling 2022-03-09 22:35:10
1
// MIT Licence
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
2
78da73c Jakob Wakeling 2023-12-28 16:23:49
3
#include "util/log.h"
6740ebe Jakob Wakeling 2022-03-09 22:34:21
4
#include "util/optget.h"
6740ebe Jakob Wakeling 2022-03-09 22:34:21
5
#include "util/util.h"
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
6
78da73c Jakob Wakeling 2023-12-28 16:23:49
7
#include <errno.h>
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
8
#include <stdio.h>
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
9
#include <stdlib.h>
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
10
#include <string.h>
e2b8eb1 Jakob Wakeling 2020-08-28 20:29:35
11
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
12
static inline int cmp(const char *s1, const char *s2);
6080736 Jakob Wakeling 2024-01-27 14:18:37
13
static inline int run(const char *b, const u64 *c, uptr len);
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
14
78da73c Jakob Wakeling 2023-12-28 16:23:49
15
static const char *const help;
78da73c Jakob Wakeling 2023-12-28 16:23:49
16
static const char *const version;
e2b8eb1 Jakob Wakeling 2020-08-28 20:29:35
17
78da73c Jakob Wakeling 2023-12-28 16:23:49
18
int main(int ac, char *av[]) {
78da73c Jakob Wakeling 2023-12-28 16:23:49
19
	struct opt opt = OPTGET_INIT; opt.str = "l:"; opt.lops = (struct lop[]){
78da73c Jakob Wakeling 2023-12-28 16:23:49
20
		{ "help",    ARG_NUL, 256 },
6080736 Jakob Wakeling 2024-01-27 14:18:37
21
		{ "version", ARG_NUL, 257 },
78da73c Jakob Wakeling 2023-12-28 16:23:49
22
		{ NULL, 0, 0 }
78da73c Jakob Wakeling 2023-12-28 16:23:49
23
	};
6080736 Jakob Wakeling 2024-01-27 14:18:37
24
6080736 Jakob Wakeling 2024-01-27 14:18:37
25
	struct { uptr l; } args = { .l = 30000 };
6080736 Jakob Wakeling 2024-01-27 14:18:37
26
e2b8eb1 Jakob Wakeling 2020-08-28 20:29:35
27
	for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) {
35b4ead Jakob Wakeling 2021-08-18 16:25:09
28
	case 'l': {
35b4ead Jakob Wakeling 2021-08-18 16:25:09
29
		register char *s = opt.arg; register int c;
6080736 Jakob Wakeling 2024-01-27 14:18:37
30
		for (args.l = 0; *s >= '0' && *s <= '9'; s += 1) { c = *s - '0';
6080736 Jakob Wakeling 2024-01-27 14:18:37
31
			if (args.l > (UPTR_MAX - c) / 10) { break; } args.l = args.l * 10 + c;
35b4ead Jakob Wakeling 2021-08-18 16:25:09
32
		}
6080736 Jakob Wakeling 2024-01-27 14:18:37
33
78da73c Jakob Wakeling 2023-12-28 16:23:49
34
		if (*s) { log_fatal(-1, "%s: invalid tape length", opt.arg); } break;
78da73c Jakob Wakeling 2023-12-28 16:23:49
35
	} break;
78da73c Jakob Wakeling 2023-12-28 16:23:49
36
	case 256: { fputs(help, stdout);    } return 0;
78da73c Jakob Wakeling 2023-12-28 16:23:49
37
	case 257: { fputs(version, stdout); } return 0;
78da73c Jakob Wakeling 2023-12-28 16:23:49
38
	default: {} return -1;
e2b8eb1 Jakob Wakeling 2020-08-28 20:29:35
39
	}
6080736 Jakob Wakeling 2024-01-27 14:18:37
40
78da73c Jakob Wakeling 2023-12-28 16:23:49
41
	if (opt.ind == ac) { log_fatal(-1, "missing operand"); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
42
6080736 Jakob Wakeling 2024-01-27 14:18:37
43
	/* Open and read source file */
6080736 Jakob Wakeling 2024-01-27 14:18:37
44
	FILE *file = fopen(av[opt.ind], "r");
6080736 Jakob Wakeling 2024-01-27 14:18:37
45
	if (!file) { log_fatal(-1, "%s: %s", av[opt.ind], strerror(errno)); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
46
6080736 Jakob Wakeling 2024-01-27 14:18:37
47
	fseek(file, 0, SEEK_END); uptr fl = ftell(file); rewind(file);
6080736 Jakob Wakeling 2024-01-27 14:18:37
48
6080736 Jakob Wakeling 2024-01-27 14:18:37
49
	/* Instruction operators */
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
50
	char *fb = malloc(fl + 1 * sizeof (*fb)); fb[fl] = 0;
78da73c Jakob Wakeling 2023-12-28 16:23:49
51
	if (!fb) { log_fatal(-1, "%s", strerror(errno)); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
52
6080736 Jakob Wakeling 2024-01-27 14:18:37
53
	/* Instruction operands */
6080736 Jakob Wakeling 2024-01-27 14:18:37
54
	u64 *fc = malloc(fl * sizeof (*fc));
78da73c Jakob Wakeling 2023-12-28 16:23:49
55
	if (!fc) { log_fatal(-1, "%s", strerror(errno)); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
56
6080736 Jakob Wakeling 2024-01-27 14:18:37
57
	fread(fb, 1, fl, file); fclose(file);
6080736 Jakob Wakeling 2024-01-27 14:18:37
58
78da73c Jakob Wakeling 2023-12-28 16:23:49
59
	/* Remove comments from instruction buffer */
6080736 Jakob Wakeling 2024-01-27 14:18:37
60
	register char *p = fb, *q = fb; for (; *p; p += 1) {
6080736 Jakob Wakeling 2024-01-27 14:18:37
61
		if (strchr("><+-.,[]", *p)) { *q = *p; q += 1; }
6080736 Jakob Wakeling 2024-01-27 14:18:37
62
	} *q = 0;
6080736 Jakob Wakeling 2024-01-27 14:18:37
63
6080736 Jakob Wakeling 2024-01-27 14:18:37
64
	register uptr i, j;
6080736 Jakob Wakeling 2024-01-27 14:18:37
65
	for (i = 0; fb[i]; i += 1) {
78da73c Jakob Wakeling 2023-12-28 16:23:49
66
	switch (fb[i]) { /* Generate initial operands */
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
67
	case '<': case '-': { fc[i] = -1; break; }
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
68
	default:            { fc[i] =  1; break; }
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
69
	}
78da73c Jakob Wakeling 2023-12-28 16:23:49
70
	switch (fb[i]) { /* Standardise instructions */
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
71
	case '>': case '<': { fb[i] = '>'; break; }
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
72
	case '+': case '-': { fb[i] = '+'; break; }
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
73
	}
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
74
	}
6080736 Jakob Wakeling 2024-01-27 14:18:37
75
78da73c Jakob Wakeling 2023-12-28 16:23:49
76
	/* Compress movement and additive instructions */
6080736 Jakob Wakeling 2024-01-27 14:18:37
77
	for (i = 0, j = 0; fb[i]; j += 1) {
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
78
		fb[j] = fb[i]; fc[j] = fc[i];
6080736 Jakob Wakeling 2024-01-27 14:18:37
79
		if (strchr(">+", fb[i += 1])) for (; fb[j] == fb[i]; fc[j] += fc[i], i += 1);
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
80
	} fb[j] = 0;
6080736 Jakob Wakeling 2024-01-27 14:18:37
81
6080736 Jakob Wakeling 2024-01-27 14:18:37
82
	for (i = 0; fb[i]; i += 1) {
78da73c Jakob Wakeling 2023-12-28 16:23:49
83
		/* Optimise set to zero loops */
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
84
		if (cmp(fb + i, "[+]")) { fb[i] = 'Z'; fb[i + 1] = fb[i + 2] = ' '; }
6080736 Jakob Wakeling 2024-01-27 14:18:37
85
78da73c Jakob Wakeling 2023-12-28 16:23:49
86
		/* Optimise move to zero loops */
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
87
		else if (cmp(fb + i, "[>]")) {
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
88
			fb[i] = 'T'; fb[i + 1] = fb[i + 2] = ' '; fc[i] = fc[i + 1];
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
89
		}
6080736 Jakob Wakeling 2024-01-27 14:18:37
90
78da73c Jakob Wakeling 2023-12-28 16:23:49
91
		/* Optimise backward and forward move to loops */
6080736 Jakob Wakeling 2024-01-27 14:18:37
92
		else if (cmp(fb + i, "[+>+>]") && fc[i + 1] == -1 && fc[i + 3] == 1 && fc[i + 2] == -fc[i + 4]) {
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
93
			fb[i] = 'M';
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
94
			fb[i + 1] = fb[i + 2] = fb[i + 3] = fb[i + 4] = fb[i + 5] = ' ';
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
95
			fc[i] = fc[i + 2];
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
96
		}
6080736 Jakob Wakeling 2024-01-27 14:18:37
97
		else if (cmp(fb + i, "[>+>+]") && fc[i + 4] == -1 && fc[i + 2] == 1 && fc[i + 1] == -fc[i + 3]) {
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
98
			fb[i] = 'M';
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
99
			fb[i + 1] = fb[i + 2] = fb[i + 3] = fb[i + 4] = fb[i + 5] = ' ';
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
100
			fc[i] = fc[i + 1];
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
101
		}
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
102
	}
6080736 Jakob Wakeling 2024-01-27 14:18:37
103
78da73c Jakob Wakeling 2023-12-28 16:23:49
104
	/* Remove resultant spaces of loop optimisations */
6080736 Jakob Wakeling 2024-01-27 14:18:37
105
	for (i = 0, j = 0; fb[i]; i += 1) if (fb[i] != ' ') {
6080736 Jakob Wakeling 2024-01-27 14:18:37
106
		fb[j] = fb[i]; fc[j] = fc[i]; j += 1;
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
107
	} fb[j] = 0;
6080736 Jakob Wakeling 2024-01-27 14:18:37
108
6080736 Jakob Wakeling 2024-01-27 14:18:37
109
	uptr l = 1024, t = 0, *S = malloc(l * sizeof (*S));
78da73c Jakob Wakeling 2023-12-28 16:23:49
110
	if (!S) { log_fatal(-1, "%s", strerror(errno)); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
111
78da73c Jakob Wakeling 2023-12-28 16:23:49
112
	/* Find and store bracket pairs */
6080736 Jakob Wakeling 2024-01-27 14:18:37
113
	for (uptr i = 0; fb[i]; i += 1) switch (fb[i]) {
6080736 Jakob Wakeling 2024-01-27 14:18:37
114
	case '[': { S[t] = i; t += 1; break; }
27e3d63 Jakob Wakeling 2021-08-18 16:27:31
115
	case ']': { --t; fc[S[t]] = i - S[t]; fc[i] = S[t] - i; break; }
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
116
	}
6080736 Jakob Wakeling 2024-01-27 14:18:37
117
78da73c Jakob Wakeling 2023-12-28 16:23:49
118
	if (run(fb, fc, args.l)) { log_fatal(-1, "%s", strerror(errno)); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
119
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
120
	free(S); free(fb); free(fc); return 0;
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
121
}
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
122
78da73c Jakob Wakeling 2023-12-28 16:23:49
123
/* Check if s2 is fully matched at the start of s1. */
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
124
static inline int cmp(const char *s1, const char *s2) {
6080736 Jakob Wakeling 2024-01-27 14:18:37
125
	uptr i = 0; for (; s2[i] && s1[i] == s2[i]; i += 1) {} return !s2[i];
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
126
}
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
127
78da73c Jakob Wakeling 2023-12-28 16:23:49
128
/* Execute an array of Brainfuck instructions. */
6080736 Jakob Wakeling 2024-01-27 14:18:37
129
static inline int run(const char *fb, const u64 *fc, uptr len) {
6080736 Jakob Wakeling 2024-01-27 14:18:37
130
	u8 *M = calloc(len, sizeof (*M)), *p = M + 256; /* Allow slight movement into "negative" memory */
78da73c Jakob Wakeling 2023-12-28 16:23:49
131
	if (!M) { log_fatal(-1, "%s", strerror(errno)); }
6080736 Jakob Wakeling 2024-01-27 14:18:37
132
6080736 Jakob Wakeling 2024-01-27 14:18:37
133
	for (uptr i = 0; fb[i]; i += 1) switch (fb[i]) {
6080736 Jakob Wakeling 2024-01-27 14:18:37
134
	case '>': { p += fc[i]; } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
135
	case '+': { *p += fc[i]; } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
136
	case '.': { fputc(*p, stdout); } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
137
	case ',': { int c = fgetc(stdin); *p = c != EOF ? c : 0; } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
138
	case '[': { i += !*p ? fc[i] : 0; } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
139
	case ']': { i +=  *p ? fc[i] : 0; } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
140
	case 'Z': { *p = 0; } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
141
	case 'T': { for (; *p; p += fc[i]); } break;
6080736 Jakob Wakeling 2024-01-27 14:18:37
142
	case 'M': { *(p + fc[i]) += *p; *p = 0; } break;
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
143
	}
6080736 Jakob Wakeling 2024-01-27 14:18:37
144
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
145
	free(M); return 0;
1dbad8b Jakob Wakeling 2020-07-14 13:19:48
146
}
e2b8eb1 Jakob Wakeling 2020-08-28 20:29:35
147
78da73c Jakob Wakeling 2023-12-28 16:23:49
148
static const char *const help =
78da73c Jakob Wakeling 2023-12-28 16:23:49
149
	"OBFI - Brainfuck Interpreter\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
150
	"Usage:\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
151
	"  obfi file\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
152
	"Options:\n"
6080736 Jakob Wakeling 2024-01-27 14:18:37
153
	"  -l length  Set tape length\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
154
	"  --help     Display help information\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
155
	"  --version  Display version information\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
156
;
e2b8eb1 Jakob Wakeling 2020-08-28 20:29:35
157
78da73c Jakob Wakeling 2023-12-28 16:23:49
158
static const char *const version =
6080736 Jakob Wakeling 2024-01-27 14:18:37
159
	"OBFI, version " VERSION "\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
160
	"Copyright (C) 2020, Jakob Wakeling\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
161
	"MIT Licence (https://opensource.org/licenses/MIT)\n"
78da73c Jakob Wakeling 2023-12-28 16:23:49
162
;
163