correct a test
[brainfuck.git] / bf.c
1 /* depend: */
2 /* cflags: */
3 /* linker: debug.o */
4
5 #include <errno.h>
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
17 #define MAXJUMP 4
18 #define MEMSIZE 10
19
20 /* type definition */
21
22 /* gobal variables */
23
24 char *progname = NULL;
25 int p = 0;
26 char mem[MEMSIZE] = {0};
27 int j = 0;
28 int jump[MAXJUMP] = {0};
29
30 /* help function */
31
32 int usage (int ret)
33 {
34 FILE *fd = ret ? stderr : stdout;
35 fprintf (fd, "usage: %s [-i file] [-h] [-m memory] [-o file] [-v]\n", progname);
36 fprintf (fd, " -i : input file\n");
37 fprintf (fd, " -h : help message\n");
38 fprintf (fd, " -m : memory [0..0]\n");
39 fprintf (fd, " -o : output file\n");
40 fprintf (fd, " -v : verbose level (%d)\n", verbose);
41
42 return ret;
43 }
44
45 /* main process */
46
47 int process (char *buffer, int nb, FILE *out) {
48 int i;
49
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"));
56
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)) {
90 int c = getchar ();
91 mem[p] = (c > 0) ? c : 0;
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 */
98 if ((p < 0) || (p >= MEMSIZE)) {
99 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
100 return 1;
101 }
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 }
111 i--;
112 } else {
113 if (j >= MAXJUMP) {
114 VERBOSE (ERROR, fprintf (stderr, "%s: too many jump\n", progname));
115 return 1;
116 }
117 jump[j++] = i;
118 }
119 break;
120 case ']': /* jump to left bracket if pointer is different to 0 */
121 if (j <= 0) {
122 VERBOSE (ERROR, fprintf (stderr, "%s: can't jump back\n", progname));
123 return 1;
124 }
125 i = jump[--j] - 1;
126 break;
127 case ' ':
128 case '\t':
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
143 /* main function */
144
145 int main (int argc, char *argv[])
146 {
147 char *input = NULL;
148 char *output = NULL;
149 char *buffer = NULL;
150 int i;
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;
165 while ((c = getopt(argc, argv, "e:i:m:o:hv:")) != EOF) {
166 switch (c) {
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;
178 case 'i':
179 input = optarg;
180 break;
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) {
189 VERBOSE (WARNING, fprintf (stderr, "%s: too many memory values or incorrect value\n", progname));
190 return 1;
191 }
192 break;
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 }
216 } else if (buffer == NULL) {
217 fid = stdin;
218 }
219
220 /* read input file */
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;
235 }
236
237 /* close input file */
238 if (fid != stdin) {
239 fclose (fid);
240 }
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 }
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));
251 if (buffer) free (buffer);
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 */
262 if (fid != stdout) {
263 fclose (fid);
264 }
265
266 /* free buffer */
267 if (buffer) {
268 free (buffer);
269 }
270
271 VERBOSE (INFO, fprintf (stdout, "\nmemory:"); int _i; for (_i = 0; _i < MEMSIZE; _i++) fprintf (stdout," %d", mem[_i]); fprintf (stdout,"\n"));
272
273 return rc;
274 }
275
276 // test: bf.exe -h
277 // test: bf.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
278
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) }'
281 // test: bf.exe error 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
282
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"
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
297 // test: echo '+++>++>>-<--' | bf.exe -v2 | grep -q "memory: 3 2 -2 -1 0"
298 // test: bf.exe -e '+++' -e '+++>++>>-<--' -v2 | grep -q "memory: 3 2 -2 -1 0"
299 // test: bf.exe -e '+++>++>>-<--' -v2 | grep -q "memory: 3 2 -2 -1 0"
300 // test: bf.exe -m "51 50 49" -e '.>.>.' |grep -q "321"
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
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
303
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"
306
307 // test: echo -e "123\0" | bf.exe -e ',[>,]' -v2 | grep -q "memory: 49 50 51 0"
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
312
313 /* vim: set ts=4 sw=4 et: */