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