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