0e331d9c4d594c38371698cbf92100b5cf9b81c9
[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 mem[p] = buffer[++i];
84 } else {
85 VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
86 return 1;
87 }
88 break;
89 case '[': /* jump to right bracket if pointer is set to 0 */
90 if (mem[p] == 0) {
91 int bracket = 1;
92 while ((++i < nb) && (bracket > 0)) {
93 bracket += (buffer[i] == '[') ? +1 : (buffer[i] == ']') ? -1 :0;
94 }
95 if (bracket) {
96 VERBOSE (ERROR, fprintf (stderr, "%s: brace not closed\n", progname));
97 return 1;
98 }
99 } else {
100 return process (buffer + i + 1, nb - i - 1, out);
101 }
102 break;
103 case ']': /* jump to left bracket if pointer is different to 0 */
104 if (mem[p] != 0) {
105 i = -1;
106 }
107 break;
108 case '\n':
109 case '\r':
110 break;
111 default:
112 VERBOSE (WARNING, fprintf (stderr, "%s: can't understand '%c'\n", progname, buffer[i]));
113 }
114
115 //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"));
116
117 }
118
119 return 0;
120 }
121
122 /* main function */
123
124 int main (int argc, char *argv[])
125 {
126 char *input = NULL;
127 char *output = NULL;
128 char *buffer = NULL;
129 int i;
130 int n = 0;
131 int size = 0;
132 FILE *fid = NULL;
133
134 /* get basename */
135 char *pt = progname = argv[0];
136 while (*pt) {
137 if ((*pt == '/') || (*pt == '\\')) {
138 progname = pt + 1;
139 }
140 pt++;
141 }
142
143 int c;
144 while ((c = getopt(argc, argv, "i:m:o:hv:")) != EOF) {
145 switch (c) {
146 case 'i':
147 input = optarg;
148 break;
149 case 'm':
150 for (i = 0; i < MEMSIZE; i++) {
151 if (optarg) {
152 mem[i] = strtol (optarg, &optarg, 10);
153 VERBOSE (DEBUG, fprintf (stderr, "%s: mem[%d] = %d\n", progname, i, mem[i]));
154 }
155 }
156 if (*optarg != 0) {
157 VERBOSE (WARNING, fprintf (stderr, "%s: too many memory values\n", progname));
158 return 1;
159 }
160 break;
161 case 'o':
162 output = optarg;
163 break;
164 case 'v':
165 verbose = atoi (optarg);
166 break;
167 case 'h':
168 default:
169 return usage (c != 'h');
170 }
171 }
172 if (argc - optind != 0) {
173 VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind]));
174 return usage (1);
175 }
176
177 /* check input file */
178 if (input) {
179 fid = fopen (input, "r");
180 if (fid == NULL) {
181 VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for reading\n", progname, input));
182 return 1;
183 }
184 } else {
185 fid = stdin;
186 }
187
188 /* read input file */
189 while (!feof (fid)) {
190 buffer = (char *) realloc (buffer, size + BUFSIZE);
191 if (buffer == NULL) {
192 VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname));
193 return 1;
194 }
195 memset (buffer + size, 0, BUFSIZE);
196 n = fread (buffer + size, 1, BUFSIZE, fid);
197 if (errno != 0) {
198 VERBOSE (ERROR, fprintf (stderr, "%s: can't read data from file '%s'\n", progname, input));
199 return 1;
200 }
201 size += BUFSIZE;
202 }
203
204 /* close input file */
205 fclose (fid);
206 VERBOSE (DEBUG, fprintf (stderr, "%s: read %d bytes\n", progname, size + n - BUFSIZE));
207
208 /* check output file */
209 if (output) {
210 fid = fopen (output, "w");
211 if (fid == NULL) {
212 VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for writing\n", progname, output));
213 return 1;
214 }
215 } else {
216 fid = stdout;
217 }
218
219 /* main process */
220 int rc = process (buffer, size, fid);
221 VERBOSE (INFO, fprintf (stdout, "\nmemory:"); int _i; for (_i = 0; _i < MEMSIZE; _i++) fprintf (stdout," %d", mem[_i]); fprintf (stdout,"\n"));
222
223 /* close output file */
224 fclose (fid);
225
226 return rc;
227 }
228
229 // test: bf.exe -h
230 // test: bf.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
231 // test: bf.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
232 // test: bf.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
233 // test: echo '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' | bf.exe -v1 | grep -q "Hello World!"
234 // test: echo '++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++++++++++++++.>+++++++++++++++++.<<++.>+++++++++++++.>--.<<.>+++.+.--.>----.++++++.+.<++.>----.++.<<.>>+.-------.<<.>>++.<.>+++++.<<.>-.+.<.>---.>---.<-.++++++++.>----.<---.>+++++++.<---.++++++++.' | bf.exe -v1 | grep -q "Tu as decouvert un peu de brainfuck"
235 // test: echo ',4>++++++[<-------->-],3[<+>-]<.' | bf.exe -v1 | grep -q 7
236
237 // test: echo ',3>,2>>++++++++++++++++[-<+++<---<--->>>]<<[<[>>+>+<<<-]>>>[<<<+>>>-]<<-]>.' | bf.exe -v1 | grep -q 6
238 // test: echo ',3>,2[->>+<<]+<[->>>[>>>>>+<<<<<->+<<]<[>]>>>>>[-<<->>>>>>>]<[>]<<<<<<]>[>>>>->]<<<<<<[-]>[-]>>[-]>.' | bf.exe -v1 | grep -q 3
239
240 /* vim: set ts=4 sw=4 et: */