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
|
|