correct a test
[brainfuck.git] / bf.c
CommitLineData
048e6e55
LM
1/* depend: */
2/* cflags: */
3/* linker: debug.o */
4
a213b0ae 5#include <errno.h>
048e6e55
LM
6#include <getopt.h>
7#include <malloc.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "debug.h"
13
14/* macros */
15
16#define BUFSIZE 256
3d8520e3
LM
17#define MAXJUMP 4
18#define MEMSIZE 10
048e6e55
LM
19
20/* type definition */
21
22/* gobal variables */
23
24char *progname = NULL;
25int p = 0;
26char mem[MEMSIZE] = {0};
3d8520e3
LM
27int j = 0;
28int jump[MAXJUMP] = {0};
048e6e55
LM
29
30/* help function */
31
32int usage (int ret)
33{
34 FILE *fd = ret ? stderr : stdout;
e07f5d2a 35 fprintf (fd, "usage: %s [-i file] [-h] [-m memory] [-o file] [-v]\n", progname);
048e6e55
LM
36 fprintf (fd, " -i : input file\n");
37 fprintf (fd, " -h : help message\n");
e07f5d2a 38 fprintf (fd, " -m : memory [0..0]\n");
048e6e55
LM
39 fprintf (fd, " -o : output file\n");
40 fprintf (fd, " -v : verbose level (%d)\n", verbose);
41
42 return ret;
43}
44
07cc2c73
LM
45/* main process */
46
47int process (char *buffer, int nb, FILE *out) {
48 int i;
49
3d8520e3
LM
50 for (i = 0; i < nb; i++) {
51 if (buffer[i] == 0) {
52 break;
53 }
54
55 VERBOSE (DEBUG, fprintf (stderr, "%s: p=%d, buffer='%s', memory=[ ", progname, p, buffer + i); int _i; for (_i = 0; _i < MEMSIZE; _i++) fprintf (stderr," %d", mem[_i]); fprintf (stderr," ]\n"));
07cc2c73 56
07cc2c73
LM
57 switch (buffer[i]) {
58 case '>': /* increase pointer */
59 p++;
60 break;
61 case '<': /* decrease pointer */
62 p--;
63 break;
64 case '+': /* increase pointer value */
65 if ((p >= 0) && (p < MEMSIZE)) {
66 mem[p]++;
67 } else {
68 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
69 return 1;
70 }
71 break;
72 case '-': /* decrease pointer value */
73 if ((p >= 0) && (p < MEMSIZE)) {
74 mem[p]--;
75 } else {
76 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
77 return 1;
78 }
79 break;
80 case '.': /* output pointer value */
81 if ((p >= 0) && (p < MEMSIZE)) {
82 fprintf (out, "%c", mem[p]);
83 } else {
84 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
85 return 1;
86 }
87 break;
88 case ',': /* read a byte and store it in memory */
89 if ((p >= 0) && (p < MEMSIZE)) {
096fb1e3
LM
90 int c = getchar ();
91 mem[p] = (c > 0) ? c : 0;
07cc2c73
LM
92 } else {
93 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
94 return 1;
95 }
96 break;
97 case '[': /* jump to right bracket if pointer is set to 0 */
3d8520e3
LM
98 if ((p < 0) || (p >= MEMSIZE)) {
99 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
100 return 1;
101 }
07cc2c73
LM
102 if (mem[p] == 0) {
103 int bracket = 1;
104 while ((++i < nb) && (bracket > 0)) {
105 bracket += (buffer[i] == '[') ? +1 : (buffer[i] == ']') ? -1 :0;
106 }
107 if (bracket) {
108 VERBOSE (ERROR, fprintf (stderr, "%s: brace not closed\n", progname));
109 return 1;
110 }
3d8520e3 111 i--;
07cc2c73 112 } else {
3d8520e3
LM
113 if (j >= MAXJUMP) {
114 VERBOSE (ERROR, fprintf (stderr, "%s: too many jump\n", progname));
115 return 1;
116 }
117 jump[j++] = i;
07cc2c73
LM
118 }
119 break;
120 case ']': /* jump to left bracket if pointer is different to 0 */
3d8520e3
LM
121 if (j <= 0) {
122 VERBOSE (ERROR, fprintf (stderr, "%s: can't jump back\n", progname));
123 return 1;
07cc2c73 124 }
3d8520e3 125 i = jump[--j] - 1;
07cc2c73 126 break;
096fb1e3
LM
127 case ' ':
128 case '\t':
07cc2c73
LM
129 case '\n':
130 case '\r':
131 break;
132 default:
133 VERBOSE (WARNING, fprintf (stderr, "%s: can't understand '%c'\n", progname, buffer[i]));
134 }
135
136 //VERBOSE (DEBUG, int _i; fprintf (stderr, "%s: p: %d mem:", progname, p); for (_i = 0; _i < MEMSIZE; _i++) fprintf (stderr, " %d", mem[_i]); fprintf (stderr, "\n"));
137
138 }
139
140 return 0;
141}
142
048e6e55
LM
143/* main function */
144
145int main (int argc, char *argv[])
146{
147 char *input = NULL;
148 char *output = NULL;
149 char *buffer = NULL;
e07f5d2a 150 int i;
048e6e55
LM
151 int n = 0;
152 int size = 0;
153 FILE *fid = NULL;
154
155 /* get basename */
156 char *pt = progname = argv[0];
157 while (*pt) {
158 if ((*pt == '/') || (*pt == '\\')) {
159 progname = pt + 1;
160 }
161 pt++;
162 }
163
164 int c;
096fb1e3 165 while ((c = getopt(argc, argv, "e:i:m:o:hv:")) != EOF) {
048e6e55 166 switch (c) {
096fb1e3
LM
167 case 'e':
168 if (buffer) {
169 free (buffer);
170 }
171 buffer = strdup (optarg);
172 if (buffer == NULL) {
173 VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname));
174 return 1;
175 }
176 size = strlen (buffer) + 1;
177 break;
048e6e55
LM
178 case 'i':
179 input = optarg;
180 break;
e07f5d2a
LM
181 case 'm':
182 for (i = 0; i < MEMSIZE; i++) {
183 if (optarg) {
184 mem[i] = strtol (optarg, &optarg, 10);
185 VERBOSE (DEBUG, fprintf (stderr, "%s: mem[%d] = %d\n", progname, i, mem[i]));
186 }
187 }
188 if (*optarg != 0) {
32237385 189 VERBOSE (WARNING, fprintf (stderr, "%s: too many memory values or incorrect value\n", progname));
e07f5d2a
LM
190 return 1;
191 }
192 break;
048e6e55
LM
193 case 'o':
194 output = optarg;
195 break;
196 case 'v':
197 verbose = atoi (optarg);
198 break;
199 case 'h':
200 default:
201 return usage (c != 'h');
202 }
203 }
204 if (argc - optind != 0) {
205 VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind]));
206 return usage (1);
207 }
208
209 /* check input file */
210 if (input) {
211 fid = fopen (input, "r");
212 if (fid == NULL) {
213 VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for reading\n", progname, input));
214 return 1;
215 }
096fb1e3 216 } else if (buffer == NULL) {
048e6e55
LM
217 fid = stdin;
218 }
219
220 /* read input file */
096fb1e3
LM
221 if (fid) {
222 while (!feof (fid)) {
223 buffer = (char *) realloc (buffer, size + BUFSIZE);
224 if (buffer == NULL) {
225 VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname));
226 return 1;
227 }
228 memset (buffer + size, 0, BUFSIZE);
229 n = fread (buffer + size, 1, BUFSIZE, fid);
230 if (errno != 0) {
231 VERBOSE (ERROR, fprintf (stderr, "%s: can't read data from file '%s'\n", progname, input));
232 return 1;
233 }
234 size += BUFSIZE;
048e6e55 235 }
048e6e55 236
096fb1e3 237 /* close input file */
3d8520e3
LM
238 if (fid != stdin) {
239 fclose (fid);
240 }
096fb1e3
LM
241 VERBOSE (DEBUG, fprintf (stderr, "%s: read %d bytes\n", progname, size + n - BUFSIZE));
242 } else {
243 VERBOSE (DEBUG, fprintf (stderr, "%s: prog %d bytes\n", progname, size -1));
244 }
048e6e55
LM
245
246 /* check output file */
247 if (output) {
248 fid = fopen (output, "w");
249 if (fid == NULL) {
250 VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for writing\n", progname, output));
a213b0ae 251 if (buffer) free (buffer);
048e6e55
LM
252 return 1;
253 }
254 } else {
255 fid = stdout;
256 }
257
258 /* main process */
259 int rc = process (buffer, size, fid);
260
261 /* close output file */
3d8520e3
LM
262 if (fid != stdout) {
263 fclose (fid);
264 }
265
a213b0ae
LM
266 /* free buffer */
267 if (buffer) {
268 free (buffer);
269 }
270
3d8520e3 271 VERBOSE (INFO, fprintf (stdout, "\nmemory:"); int _i; for (_i = 0; _i < MEMSIZE; _i++) fprintf (stdout," %d", mem[_i]); fprintf (stdout,"\n"));
048e6e55
LM
272
273 return rc;
274}
275
048e6e55
LM
276// test: bf.exe -h
277// test: bf.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
32237385 278
048e6e55
LM
279// test: bf.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
280// test: bf.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
32237385
LM
281// test: bf.exe error 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
282
096fb1e3
LM
283// test: bf.exe -i error.b 2>&1 | grep "can't open" | grep -q "reading"
284// test: echo ">>." | bf.exe -o error/error.b 2>&1 | grep "can't open" | grep -q "writing"
32237385
LM
285// test: echo "error" | bf.exe -v1 2>&1 | grep -q "can't understand"
286// test: echo "<+" | bf.exe -v1 2>&1 | grep -q "invalid address"
287// test: echo "<-" | bf.exe -v1 2>&1 | grep -q "invalid address"
288// test: echo "<." | bf.exe -v1 2>&1 | grep -q "invalid address"
289// test: echo "<," | bf.exe -v1 2>&1 | grep -q "invalid address"
290// test: echo "<[" | bf.exe -v1 2>&1 | grep -q "invalid address"
291// test: echo "[" | bf.exe -v1 2>&1 | grep -q "brace not closed"
292// test: echo "+[[[[[]]]]]" | bf.exe -v1 2>&1 | grep -q "too many jump"
293// test: bf.exe -m "0 1 2 3 4 5 6 7 8 9 10" -e '.' -v1 2>&1 | grep -q "too many memory values"
294// test: bf.exe -m "0 error" -e '.' -v1 2>&1 | grep -q "incorrect value"
295// test: echo "]" | bf.exe -v1 2>&1 | grep -q "can't jump back"
296
096fb1e3 297// test: echo '+++>++>>-<--' | bf.exe -v2 | grep -q "memory: 3 2 -2 -1 0"
32237385 298// test: bf.exe -e '+++' -e '+++>++>>-<--' -v2 | grep -q "memory: 3 2 -2 -1 0"
096fb1e3 299// test: bf.exe -e '+++>++>>-<--' -v2 | grep -q "memory: 3 2 -2 -1 0"
3d8520e3 300// test: bf.exe -m "51 50 49" -e '.>.>.' |grep -q "321"
20c60362 301// test: bf.exe -m "51 50 49" -e '.>.>.' -o test.log && grep -q "321" test.log; rc=$?; rm test.log; test $rc -eq 0
ee55e48c 302// test: echo '+++>++>>-<--' > test.b; bf.exe -i test.b | grep -q "memory: 3 2 -2 -1 0"; rc=$?; rm test.b; test $rc -eq 0
32237385 303
e07f5d2a
LM
304// test: echo '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' | bf.exe -v1 | grep -q "Hello World!"
305// test: echo '++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++++++++++++++.>+++++++++++++++++.<<++.>+++++++++++++.>--.<<.>+++.+.--.>----.++++++.+.<++.>----.++.<<.>>+.-------.<<.>>++.<.>+++++.<<.>-.+.<.>---.>---.<-.++++++++.>----.<---.>+++++++.<---.++++++++.' | bf.exe -v1 | grep -q "Tu as decouvert un peu de brainfuck"
32237385 306
096fb1e3 307// test: echo -e "123\0" | bf.exe -e ',[>,]' -v2 | grep -q "memory: 49 50 51 0"
3d8520e3
LM
308// test: echo -e "4+3\0" | bf.exe -e ',>++++++[<-------->-],,[<+>-]<.' -v1 | grep -q 7
309// test: echo -e "1+7\0" | bf.exe -e ',>++++++[<-------->-],,[<+>-]<.' -v1 | grep -q 8
310// test: echo -e "3*2\0" | bf.exe -e ',>,,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-]<.' -v1 | grep -q 6
311// test: echo -e "1*7\0" | bf.exe -e ',>,,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-]<.' -v1 | grep -q 7
048e6e55
LM
312
313/* vim: set ts=4 sw=4 et: */