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