Brainfuck Interpreter in C (Revised)

A revised version of the brainfuck interpreter I wrote, both the original and this revised version, back in 2005. A debug mode was added by request of a friend, as well as an interactive interface for debugging, with features like breakpoints, tracing, single-stepping, etc.

I also converted it into some sort of C++, more precisely, it's still C in the guise of C++, with many rough edges and redundancies. C style coding with C++ syntax is a practice considered harmful by some. While I agree with that, the reason behind this is that I just needed a way to organize many functions that share a few states, which C++'s namespace's and class'es are really good at. I also did not intend to invest on it more than necessary, considering it is merely a toy and refactoring into a full-fledged C++ program with adequate C++ patterns and STL, etc. takes time.

Without further ado, these are the source:

Makefile:

 1 # by eightpm at baidu c bar
 2 # May 17, 05
 3 # added switch -Wall for all warning.
 4 
 5 CC := gcc
 6 CXX := g++
 7 
 8 CFLAGS := -Wall -O2
 9 CXXFLAGS := -Wall -O2
10 
11 BFI_OBJS = bfi.o bfimain.o
12 BFI_HEADERS = bfi.h
13 BFI_EXEC = bfi
14 
15 .PHONY : all
16 all: $(BFI_EXEC)
17 
18 $(BFI_EXEC): $(BFI_OBJS)
19         $(CXX) $(BFI_OBJS) -o $(BFI_EXEC)
20 
21 $(BFI_OBJS): $(BFI_HEADERS)
22 
23 .PHONY : clean
24 clean:
25         rm -f $(BFI_EXEC) $(BFI_OBJS) *~
26 
27 .PHONY : install
28 install: all
29         mv $(BFI_EXEC) /usr/local/bin
30 
31 #end of Makefile
32 

bfi.h:

  1 #if !defined(gem_hdr_bfi)
  2 #define gem_hdr_bfi
  3 
  4 #define gem_bfi_version_major 1
  5 #define gem_bfi_version_minor 0
  6 
  7 #include <cstdio>
  8 #include <cstdlib>
  9 #include <cstring>
 10 #include <cctype>
 11 #include <cerrno>
 12 #include <climits>
 13 
 14 namespace gem {
 15         class bfi {
 16         public:
 17                 bfi(int argc, char **argv);
 18                 ~bfi();
 19 
 20                 bool operator!();
 21                 bool run();
 22 
 23         public:
 24                 enum {
 25                         max_cell_size = 30000,
 26                         max_code_size = 30000,
 27                         max_buffer_size = 1024,
 28                         max_breakpoint = 20,
 29                         max_path = 1024
 30                 };
 31 
 32                 enum mode {
 33                         usage,
 34                         normal,
 35                         debug,
 36                         debug_break,
 37                         unknown_switch
 38                 };
 39 
 40         protected:
 41                 void show_welcome();
 42                 void show_app_name();
 43                 void show_gpl_license();
 44                 void show_usage();
 45                 void show_debug_welcome();
 46                 void show_debug_break_welcome();
 47                 void show_unknown_switch();
 48                 void show_debug_help();
 49                 void show_debug_prompt();
 50                 void show_stat();
 51                 void show_breakpoints();
 52                 void show_message(const char *msg);
 53                 void show_cell();
 54                 void show_cell(int begin, int end);
 55                 void show_code();
 56                 void show_code(int begin, int end);
 57 
 58                 enum command {
 59                         cm_help,
 60                         cm_stat,
 61                         cm_quit,
 62                         cm_exit,
 63                         cm_restart,
 64                         cm_trace,
 65                         cm_run,
 66                         cm_step,
 67                         cm_bp,
 68                         cm_load,
 69                         cm_cell,
 70                         cm_code,
 71                         cm_unknown,
 72                         cm_max = cm_unknown,
 73                         // those following aren't real command but error code
 74                         cm_too_many,
 75                         cm_invalid,
 76                         cm_range_err
 77                 };
 78 
 79                 void parse_cmdline();
 80                 bool initialize();
 81                 bool start();
 82 
 83                 bool open_file(const char *file);
 84                 bool load_program();
 85                 void reset();
 86                 void restart();
 87                 void clear_cell();
 88                 void clear_code();
 89                 void clear_all_breakpoint();
 90                 bool add_breakpoint(const int bp);
 91                 bool delete_breakpoint(const int bp);
 92 
 93                 bool is_breakpoint(const int here);
 94 
 95                 bool normal_run();
 96                 bool debug_run();
 97 
 98                 int get_command();
 99 
100         protected:
101                 int _argc;
102                 char **_argv;
103 
104                 char _cell[max_cell_size];
105                 char _code[max_code_size];
106                 int _breakpoints[max_breakpoint];
107                 char _source_name[max_path];
108 
109                 static const char *_sc_commands[cm_max];
110 
111                 mode _running_mode;
112                 int _ip;
113                 int _dp;
114                 bool _ok;
115 
116                 // for use in debug mode only
117                 bool _trace;
118                 int _para1;
119                 int _para2;
120 
121                 std::FILE *_source;
122                 std::FILE *_in;
123                 std::FILE *_out;
124                 std::FILE *_err;
125         };
126 
127         inline bool bfi::operator!()
128         {
129                 return !_ok;
130         }
131 
132         inline void bfi::show_welcome()
133         {
134                 switch (_running_mode) {
135                 case usage:
136                         show_app_name();
137                         show_gpl_license();
138                         show_usage();
139                 case normal:
140                         // no app output
141                         break;
142                 case debug:
143                         show_app_name();
144                         show_debug_welcome();
145                         break;
146                 case debug_break:
147                         show_app_name();
148                         show_debug_welcome();
149                         show_debug_break_welcome();
150                         break;
151                 case unknown_switch:
152                         show_app_name();
153                         show_unknown_switch();
154                         break;
155                 }
156         }
157 
158         inline bool bfi::run()
159         {
160                 parse_cmdline();
161                 show_welcome();
162                 return initialize() && start();
163         }
164 
165         inline void bfi::reset()
166         {
167                 clear_cell();
168                 clear_code();
169         }
170 
171         inline void bfi::show_cell()
172         {
173                 show_cell(-1, -1);
174         }
175         inline void bfi::show_code()
176         {
177                 show_code(-1, -1);
178         }
179 
180 
181 }       // end of namespace gem
182 
183 #endif  // end of bfi.h

bfi.cpp:

  1 #include "bfi.h"
  2 
  3 namespace gem { // constructor, destructor
  4         bfi::bfi(int argc, char **argv)
  5         :       _argc(argc)
  6         ,       _argv(argv)
  7         ,       _running_mode(usage)
  8         ,       _ip(0)
  9         , _dp(0)
 10         , _ok(false)
 11         , _trace(false)
 12         , _para1(-1)
 13         , _para2(-2)
 14         , _source(stdin)
 15         , _in(stdin)
 16         , _out(stdout)
 17         , _err(stderr)
 18         {
 19                 reset();
 20         }
 21 
 22         bfi::~bfi()
 23         {
 24                 _ok = false;
 25         }
 26 
 27 }       // end of namespace gem
 28 
 29 namespace gem { // those output functions
 30         using namespace std;
 31 
 32         const char *sc_single_str = "%s";
 33         const char *sc_newline = "\r\n";
 34         const char cc_break_char = '`';
 35         const char cc_empty_code = '_';
 36 
 37         inline void bfi::show_message(const char *msg)
 38         {
 39                 fprintf(_err, "\r\n%s\r\n", msg);
 40         }
 41 
 42         inline void bfi::show_debug_prompt()
 43         {
 44                 const char *sc_debug_prompt = "\r\n[bfi] ";
 45                 fprintf(_err, sc_single_str, sc_debug_prompt);
 46         }
 47 
 48         void bfi::show_app_name()
 49         {
 50                 const char *sc_app_name =
 51                         "    bfi - brainf*ck language interpreter %d.%d build - "__DATE__"\r\n"
 52                         "           ----- copyright (c) 2005 by 8pm (eightpm at baidu c bar)\r\n";
 53                 fprintf(_err, sc_app_name, gem_bfi_version_major, gem_bfi_version_minor);
 54         }
 55 
 56         void bfi::show_gpl_license()
 57         {
 58                 const char *sc_gpl_license =
 59                         " This program is free software; you can redistribute it and/or modify\r\n"
 60                         " it under the terms of the GNU General Public License as published by\r\n"
 61                         " the Free Software Foundation; either version 2 of the License, or\r\n"
 62                         " (at your option) any later version.\r\n"
 63                         "\r\n"
 64                         " This program is distributed in the hope that it will be useful,\r\n"
 65                         " but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n"
 66                         " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n"
 67                         " GNU General Public License for more details.\r\n"
 68                         "\r\n"
 69                         " You should have received a copy of the GNU General Public License\r\n"
 70                         " along with this program; if not, write to the\r\n"
 71                         " Free Software Foundation, Inc.,\r\n"
 72                         " 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n"
 73                         "\r\n";
 74                 fprintf(_err, sc_single_str, sc_gpl_license);
 75         }
 76 
 77         void bfi::show_usage()
 78         {
 79                 const char *sc_usage =
 80                         " usage: $ %s [-d|D] [source code]\r\n"
 81                         "     d: debug mode\r\n"
 82                         "     D: debug mode(treat character \'%c\' as breakpoint)\r\n"
 83                         "     enter \"help\" in debug mode for more information\r\n"
 84                         "\r\n";
 85                 fprintf(_err, sc_usage, _argv[0], cc_break_char);
 86         }
 87 
 88         void bfi::show_debug_welcome()
 89         {
 90                 const char *sc_debug_welcome =
 91                         ":) started in debug mode, enter help for more information\r\n";
 92                 fprintf(_err, sc_single_str, sc_debug_welcome);
 93         }
 94 
 95         void bfi::show_debug_break_welcome()
 96         {
 97                 const char *sc_debug_break_welcome =
 98                         "   in -D debug mode, every \'%c\' character in the source code\r\n"
 99                         "   will be treated as break point, only the first %d is valid\r\n"
100                         "*===============================================*\r\n";
101                 fprintf(_err, sc_debug_break_welcome, cc_break_char, max_breakpoint);
102         }
103 
104         void bfi::show_unknown_switch()
105         {
106                 const char *sc_unknown_switch = ":( unknown switch";
107                 show_message(sc_unknown_switch);
108         }
109 
110         void bfi::show_debug_help()
111         {
112                 const char *sc_debug_help =
113                         "*===============================================*\r\n"
114                         "  [commands]\r\n"
115                         "  help      : display this page\r\n"
116                         "     ?      : display cell and code status\r\n"
117                         "  quit|exit : quit this program\r\n"
118                         "  restart   : restart the program\r\n"
119                         "  trace     : toggle trace display\r\n"
120                         "  run [x]   : execute the program until the end\r\n"
121                         "           or reach a break point character \'%c\'\r\n"
122                         "           or toward(before) No. x instruction\r\n"
123                         "  step [x]  : step x instructions\r\n"
124                         "    blank line (just hit enter) = step = step 1\r\n"
125                         "  bp        : list all the breakpoints\r\n"
126                         "  bp x      : add (or delete) breakpoint at x\r\n"
127                         "  load [file] : load the current file or new one\r\n"
128                         "  cell [start] [end] :\r\n"
129                         "             display the value in current cell\r\n"
130                         "             or from \"start\" to \"end\"\r\n"
131                         "  code [start] [end] :\r\n"
132                         "             display the next instruction\r\n"
133                         "             or from \"start\" to \"end\"\r\n"
134                         "*===============================================*\r\n";
135                 fprintf(_err, sc_debug_help, cc_break_char);
136         }
137 
138         void bfi::show_stat()
139         {
140                 const char *sc_debug_stat =
141                         "\r\n"
142                         "*===============================================*\r\n"
143                         "*  the next instruction is %c         at %06d  *\r\n"
144                         "*  the current cell value is 0x%02x    at %06d  *\r\n"
145                         "*===============================================*\r\n";
146 
147                 fprintf(_err, sc_debug_stat, (_code[_ip])? _code[_ip]: cc_empty_code,
148                         _ip, static_cast<unsigned char>(_cell[_dp]), _dp);
149         }
150 
151         void bfi::show_breakpoints()
152         {
153                 const char *sc_debug_bpl_hdr =
154                         "*==============* breakpoint list *==============*\r\n";
155                 const char *sc_debug_bpl_ft =
156                         "*==============* end of the list *==============*\r\n";
157                 const char *sc_debug_bp =
158                         "* %02d break point at %06d the instruction is %c *\r\n";
159 
160                 fprintf(_err, sc_single_str, sc_debug_bpl_hdr);
161                 for (int i = 0; i < max_breakpoint; ++i)
162                         if (max_code_size != _breakpoints[i])
163                                 fprintf(_err, sc_debug_bp, i, _breakpoints[i], _code[_breakpoints[i]]);
164                 fprintf(_err, sc_single_str, sc_debug_bpl_ft);
165         }
166 
167         void bfi::show_cell(int begin, int end)
168         {
169                 const char *sc_cell_hdr = "\r\n[ cell ]  ";
170                 const char *sc_cell_sep =
171                         "\r\n offset +----------------------------------------";
172                 const char *sc_cell_end =
173                         "\r\n[ cell ]+----------------------------------------\r\n";
174 
175                 if (begin < 0)
176                         begin = _dp - _dp%10;
177                 if (end < 0)
178                         end = begin + 19;
179                 if (end >= max_cell_size)
180                         end = max_cell_size-1;
181                 // the following print out the cell table header
182                 fprintf(_err, sc_single_str, sc_cell_hdr);
183                 for (int i = 0; i < 10; ++i)
184                         fprintf(_err, " %02d ", begin%10 + i);
185                 fprintf(_err, sc_single_str, sc_cell_sep);
186 
187                 // the following print out the cell table
188                 for (int i = 0; i <= end-begin; ++i) {
189                         if (0 == i%10) fprintf(_err, "\r\n %06d | ", begin+i);
190                         fprintf(_err, (_dp == begin+i)? "[%02x]": " %02x ",
191                                 static_cast<unsigned char>(_cell[begin+i]));
192                 }
193                 fprintf(_err, sc_single_str, sc_cell_end);
194         }
195 
196         void bfi::show_code(int begin, int end)
197         {
198                 const char *sc_code_hdr = "\r\n[ code ] ";
199                 const char *sc_code_sep =
200                         "\r\n offset  ----------------------------------------";
201                 bool current = false;
202 
203                 if (begin < 0)
204                         begin = _ip - _ip%40;
205                 if (end < 0)
206                         end = begin + 39;
207                 if (end >= max_code_size)
208                         end = max_code_size-1;
209                 // the following print out the code string header
210                 fprintf(_err, sc_single_str, sc_code_hdr);
211                 for (int i = 0; i < 40; ++i)
212                         fprintf(_err, "%d", (begin+i)%10);
213                 fprintf(_err, sc_single_str, sc_code_sep);
214 
215                 // the following print out the code string
216                 for (int i = 0; i <= end-begin; ++i) {
217                         if (_ip == begin+i) current = true;
218                         if (0 == i%40) fprintf(_err, "\r\n %06d  ", begin+i);
219                         fprintf(_err, "%c", (_code[begin+i]) ? _code[begin+i] : cc_empty_code);
220                         if (39 == i%40 && current) {
221                                 fprintf(_err, "\r\n[%06d] ", _ip);
222                                 fprintf(_err, "%*c", (_ip-begin)%40+1, '^');
223                                 current = false;
224                         }
225                 }
226                 fprintf(_err, sc_single_str, sc_newline);
227         }
228 
229 }       // end of namespace gem
230 
231 namespace gem { // some static members here
232 
233         const char *bfi::_sc_commands[cm_max] = {
234                 "help",
235                 "?",
236                 "quit",
237                 "exit",
238                 "restart",
239                 "trace",
240                 "run",
241                 "step",
242                 "bp",
243                 "load",
244                 "cell",
245                 "code",
246         };
247 
248 }       // end of namespace gem
249 
250 namespace gem { // operations etc.
251         using namespace std;
252 
253         void bfi::parse_cmdline()
254         {
255                 if (1 == _argc) {
256                         _running_mode = usage;
257                         return;
258                 }
259                 int n = 1;
260                 if ('-' == _argv[n][0]) {
261                         switch (_argv[n][1]) {
262                         case 'd':
263                                 _running_mode = debug;
264                                 _ok = true;
265                                 break;
266                         case 'D':
267                                 _running_mode = debug_break;
268                                 _ok = true;
269                                 break;
270                         default:
271                                 _running_mode = unknown_switch;
272                                 break;
273                         }
274                         n++;
275                 } else _running_mode = normal;
276                 strncpy(_source_name, (_argv[n]) ? _argv[n] : "", max_path-1);
277                 _source_name[max_path-1] = '\0';
278         }
279 
280         bool bfi::initialize()
281         {
282                 if (usage == _running_mode) return true;
283                 if (normal != _running_mode && !strlen(_source_name)) return true;
284                 if (open_file(_source_name))
285                         return load_program();
286                 else return false;
287         }
288 
289         bool bfi::open_file(const char *file)
290         {
291                 const char *sc_err_open =
292                         ":( error: unable to open file %s\r\n";
293                 if (NULL == (_source = fopen(file, "r"))) {
294                         fprintf(_err, sc_err_open, file);
295                         return false;
296                 }
297                 else return true;
298         }
299 
300         bool bfi::load_program()
301         {
302                 const char *sc_err_too_large =
303                         ":( error: unable to load, program too large.";
304                 const char *sc_err_unmatch =
305                         ":( error: unmatch %c in source file.\r\n";
306 
307                 int br = 0, p = 0, n = 0;
308                 reset();
309                 _ok = false;
310                 while (!feof(_source))
311                         switch (n = fgetc(_source)) {
312                         case cc_break_char:
313                                 if (debug_break == _running_mode)
314                                         add_breakpoint(p);
315                                 break;
316                         case '[': br += 2;
317                         case ']': --br;
318                         case '>':
319                         case '<':
320                         case '+':
321                         case '-':
322                         case '.':
323                         case ',':
324                                 _code[p++] = n;
325                                 if (p == max_code_size-1) { //program too large
326                                         show_message(sc_err_too_large);
327                                         return false;
328                                 }
329                         }
330                 _code[p] = '\0';
331                 if (_source != stdin) { fclose(_source); _source = NULL; }
332                 if (br) {
333                         fprintf(_err, sc_err_unmatch, (br > 0)? '[': ']');
334                         return false;
335                 } else return _ok = true, _ok;
336         }
337 
338         bool bfi::start()
339         {
340                 if (usage == _running_mode || unknown_switch == _running_mode) return true;
341                 if (normal == _running_mode)
342                         return normal_run();
343                 else
344                         return debug_run();
345         }
346 
347         void bfi::restart()
348         {
349                 clear_cell();
350                 _ip = 0;
351         }
352 
353         void bfi::clear_cell()
354         {
355                 memset(_cell, 0, max_cell_size);
356                 _dp = 0;
357                 _para1 = _para2 = -1;
358         }
359 
360         void bfi::clear_code()
361         {
362                 memset(_code, 0, max_code_size);
363                 clear_all_breakpoint();
364                 _ip = 0;
365                 _ok = false;
366         }
367 
368         void bfi::clear_all_breakpoint()
369         {
370                 for (int i = 0; i < max_breakpoint; ++i)
371                         _breakpoints[i] = max_code_size;
372         }
373 
374         bool bfi::add_breakpoint(const int bp)
375         {
376                 // see if is already exist
377                 for (int i = 0; i < max_breakpoint; ++i)
378                         if (bp == _breakpoints[i]) return true;
379                 // add it to the list, otherwise
380                 for (int i = 0; i < max_breakpoint; ++i)
381                         if (max_code_size == _breakpoints[i]) {
382                                 _breakpoints[i] = bp;
383                                 return true;
384                         }
385                 // no more space
386                 return false;
387         }
388 
389         bool bfi::delete_breakpoint(const int bp)
390         {
391                 // delete if is exist
392                 for (int i = 0; i < max_breakpoint; ++i)
393                         if (bp == _breakpoints[i]) {
394                                 _breakpoints[i] = max_code_size;
395                                 return true;
396                         }
397                 // the breakpoint not exist
398                 return false;
399         }
400 
401         bool bfi::is_breakpoint(const int here)
402         {
403                 for (int i = 0; i < max_breakpoint; ++i)
404                         if (max_breakpoint != _breakpoints[i] && here == _breakpoints[i])
405                                 return true;
406                 return false;
407         }
408 
409         bool bfi::normal_run()
410         {
411                 int n = 0;
412                 _ip = _dp = 0;
413                 do {
414                         n = 1;
415                         switch (_code[_ip]) {
416                         case '>': ++_dp == max_cell_size ? _dp = 0: 0; break;
417                         case '<': --_dp == -1 ? _dp += max_cell_size: 0; break;
418                         case '+': _cell[_dp]++; break;
419                         case '-': _cell[_dp]--; break;
420                         case '.': fputc(_cell[_dp], _out); fflush(_out); break;
421                         case ',': _cell[_dp] = fgetc(_in); break;
422                         case '[':
423                                 if (!_cell[_dp])
424                                         while (n)
425                                                 switch (_code[++_ip]) {
426                                                 case '[': n++; break;
427                                                 case ']': n--; break;
428                                                 }
429                                         break;
430                         case ']':
431                                 if (_cell[_dp])
432                                         while (n)
433                                                 switch (_code[--_ip]) {
434                                                 case ']': n++; break;
435                                                 case '[': n--; break;
436                                                 }
437                                         break;
438                         }
439                 } while (_code[++_ip]);
440                 return true;
441         }
442 
443         bool bfi::debug_run()
444         {
445                 const char *sc_debug_input = "[input for program]\a: ";
446                 const char *sc_debug_output = "[program print out]\a: ";
447                 const char *sc_debug_exit = ":) thank you for using, bye.\r\n";
448                 const char *sc_debug_restart = ":) program restarted.";
449                 const char *sc_debug_load = ":) program loaded.";
450                 const char *sc_debug_unknown = ":( error: unknown command";
451                 const char *sc_debug_del_bp_ok = ":) breakpoint deleted.";
452                 const char *sc_debug_del_bp_fl = ":( cannot delete breakpoint. contact 8pm.";
453                 const char *sc_debug_add_bp_ok = ":) breakpoint created.";
454                 const char *sc_debug_add_bp_fl = ":( cannot add breakpoint, list is full.";
455                 const char *sc_debug_bp_invalid = ":( no valid code at the address.";
456                 const char *sc_debug_too_many =
457                         ":( error: too many parameters for this command";
458                 const char *sc_debug_invalid =
459                         ":( error: the number you entered was invalid";
460                 const char *sc_debug_out_of_range =
461                         ":( error: the number you entered was out of range";
462 
463                 const char *sc_debug_over = "\r\n:) reached the end of program at %06d\r\n";
464                 const char *sc_debug_break =
465                         "\r\n:) stop before instruction %c at breakpoint %06d\r\n";
466                 const char *sc_debug_stop_at =
467                         "\r\n:) stop before instruction %c at %06d by request\r\n";
468                 const char *sc_debug_trace = "\r\n:) trace mode %s.\r\n";
469                 const char *sc_debug_single_step =
470                         "\r\n:) step. the executed instruction is %c at %06d\r\n";
471                 const char *sc_debug_steps = "\r\n:) step %d.\r\n";
472 
473                 int n = 0;
474                 int step = 0, stop_at = -1;
475                 int cm = cm_unknown;
476                 _ip = _dp = 0;
477                 while (true) {
478                         cm = get_command();
479                         step = 0;
480                         switch (cm) {
481                         case cm_help:
482                                 show_debug_help();
483                                 continue;
484                         case cm_stat:
485                                 show_stat();
486                                 show_cell();
487                                 show_code();
488                                 continue;
489                         case cm_quit:
490                         case cm_exit:
491                                 show_message(sc_debug_exit);
492                                 return true;
493                         case cm_restart:
494                                 restart();
495                                 show_message(sc_debug_restart);
496                                 continue;
497                         case cm_trace:
498                                 _trace = !_trace;
499                                 fprintf(_err, sc_debug_trace, _trace? "on": "off");
500                                 continue;
501                         case cm_run:
502                                 if (-1 != _para2) {
503                                         show_message(sc_debug_too_many);
504                                         continue;
505                                 }
506                                 if (_para1 >= max_code_size || (-1 != _para1 && !_code[_para1])) {
507                                         show_message(sc_debug_out_of_range);
508                                         continue;
509                                 }
510                                 step = -1;
511                                 stop_at = _para1;
512                                 break;
513                         case cm_step:
514                                 if (!_code[_ip]) // finish, no need to step
515                                         break;
516                                 step = _para1 < 0 ? 1 : _para1;
517                                 if (1 == step)
518                                         fprintf(_err, sc_debug_single_step, _code[_ip], _ip);
519                                 else
520                                         fprintf(_err, sc_debug_steps, step);
521                                 break;
522                         case cm_bp:
523                                 if (-1 != _para2)
524                                         show_message(sc_debug_too_many);
525                                 else if (-1 == _para1)
526                                         show_breakpoints();
527                                 else if (_para1 < 0 || _para1 >= max_code_size)
528                                         show_message(sc_debug_out_of_range);
529                                 else if (is_breakpoint(_para1)) {
530                                                 if (delete_breakpoint(_para1))
531                                                         show_message(sc_debug_del_bp_ok);
532                                                 else
533                                                         show_message(sc_debug_del_bp_fl);
534                                 } else {
535                                         if (!_code[_para1])
536                                                 show_message(sc_debug_bp_invalid);
537                                         else if (add_breakpoint(_para1))
538                                                 show_message(sc_debug_add_bp_ok);
539                                         else
540                                                 show_message(sc_debug_add_bp_fl);
541                                 }
542                                 continue;
543                         case cm_load:
544                                 if (_para1) { // get_command will set this var to non-zero if file opened
545                                         if (load_program()) show_message(sc_debug_load);
546                                         else reset();
547                                 }
548                                 continue;
549                         case cm_cell:
550                                 if (_para1 >= max_cell_size
551                                         || _para2 >= max_cell_size
552                                         || (_para2 > 0 && _para2 < _para1))
553                                         show_message(sc_debug_out_of_range);
554                                 else
555                                         show_cell(_para1, _para2);
556                                 continue;
557                         case cm_code:
558                                 if (_para1 >= max_code_size
559                                         || _para2 >= max_cell_size
560                                         || (_para2 > 0 && _para2 < _para1))
561                                         show_message(sc_debug_out_of_range);
562                                 else
563                                         show_code(_para1, _para2);
564                                 continue;
565                         case cm_too_many:
566                                 show_message(sc_debug_too_many);
567                                 continue;
568                         case cm_invalid:
569                                 show_message(sc_debug_invalid);
570                                 continue;
571                         case cm_range_err:
572                                 show_message(sc_debug_out_of_range);
573                                 continue;
574                         case cm_unknown:
575                         default:
576                                 show_message(sc_debug_unknown);
577                                 continue;
578                         }
579 
580                         while (_code[_ip] && (step < 0 || step--)) {
581                                 n = 1;
582                                 switch (_code[_ip]) {
583                                 case '>': ++_dp == max_cell_size ? _dp = 0: 0; break;
584                                 case '<': --_dp == -1 ? _dp += max_cell_size: 0; break;
585                                 case '+': _cell[_dp]++; break;
586                                 case '-': _cell[_dp]--; break;
587                                 case '.':
588                                         if (_trace && step >= 0) {
589                                                 fprintf(_err, sc_single_str, sc_debug_output);
590                                                 fflush(_err);
591                                         }
592                                         fputc(_cell[_dp], _out);
593                                         fflush(_out);
594                                         if (_trace && step >= 0) {
595                                                 fprintf(_err, sc_single_str, sc_newline);
596                                                 fflush(_err);
597                                         }
598                                         break;
599                                 case ',':
600                                         if (_trace && step >= 0)
601                                                 fprintf(_err, sc_single_str, sc_debug_input);
602                                         _cell[_dp] = fgetc(_in);
603                                         break;
604                                 case '[':
605                                         if (!_cell[_dp])
606                                                 while (n)
607                                                         switch (_code[++_ip]) {
608                                                         case '[': n++; break;
609                                                         case ']': n--; break;
610                                                         }
611                                                 break;
612                                 case ']':
613                                         if (_cell[_dp])
614                                                 while (n)
615                                                         switch (_code[--_ip]) {
616                                                         case ']': n++; break;
617                                                         case '[': n--; break;
618                                                         }
619                                                 break;
620                                 }
621                                 ++_ip;
622                                 if (is_breakpoint(_ip)) {
623                                         step = 0;
624                                         fprintf(_err, sc_debug_break, _code[_ip], _ip);
625                                 }
626                                 if (stop_at >= 0 && _ip == stop_at) {
627                                         step = 0;
628                                         stop_at = -1;
629                                         fprintf(_err, sc_debug_stop_at, _code[_ip], _ip);
630                                 }
631                         }
632                         if (!_code[_ip])
633                                 fprintf(_err, sc_debug_over, _ip);
634                         if (_trace) {
635                                 show_stat();
636                                 show_cell();
637                                 show_code();
638                         }
639                 }
640                 return true;
641         }
642 
643         int bfi::get_command()
644         {
645                 char local_buffer[max_buffer_size];
646                 char *p = local_buffer, *p2 = local_buffer;
647 
648                 show_debug_prompt();
649                 fflush(_in);
650                 fgets(local_buffer, max_buffer_size, _in);
651                 fflush(_in);
652                 _para1 = _para2 = -1;
653                 // skip the leading white spaces
654                 while (isspace(*p)) p++;
655                 if (!*p) return cm_step;
656 
657                 int index;
658                 for (index = cm_help; index < cm_max; ++index)
659                         if (0 == strncmp(p, _sc_commands[index], strlen(_sc_commands[index])))
660                                 break;
661                 if (cm_unknown == index) return index;
662                 p += strlen(_sc_commands[index]);
663                 if (!isspace(*p)) return cm_unknown;
664 
665                 // filter out all the commands that don't need a parameter
666                 switch (index) {
667                 case cm_help:
668                 case cm_stat:
669                 case cm_quit:
670                 case cm_exit:
671                 case cm_restart:
672                 case cm_trace:
673                         // skip the leading white spaces
674                         while (isspace(*p)) p++;
675                         // we don't need any paratemer here
676                         return (*p)? cm_too_many: index;
677                 case cm_load:
678                         // skip the leading white spaces
679                         while (isspace(*p)) p++;
680                         p2 = p;
681                         while (*p2) if ('\n' == *p2++) *(p2-1) = '\0';
682                         _para1 = open_file((*p) ? p : _source_name) ? 1: 0;
683                         if (_para1 && *p) {
684                                 // caution, here i assume max_local_buffer is no bigger than max_path
685                                 strncpy(_source_name, p, max_path-1);
686                                 _source_name[max_path-1] = '\0';
687                         }
688                         return cm_load;
689                 }
690                 // okay, try to read numbers
691                 if (*p) {
692                         _para1 = strtol(p, &p2, 0);
693                         if (_para1 < 0 || (LONG_MIN == _para1 && ERANGE == errno))
694                                 return cm_range_err;
695                         else if (p == p2) // no more valid input
696                                 _para1 = -1;
697                         else p = p2;
698                 }
699                 if (-1 != _para1 && *p) {
700                         _para2 = strtol(p, &p2, 0);
701                         if (_para2 < 0 || (LONG_MIN == _para2 && ERANGE == errno))
702                                 return cm_range_err;
703                         else if (p == p2) // no more valid input
704                                 _para2 = -1;
705                         else p = p2;
706                 }
707                 // check if any non_white-space is immediately follows
708                 // or any non-white-space character left,
709                 // if so, invalid input
710                 while (*p) if (!isspace(*p++))
711                         return cm_invalid;
712                 // everything seems fine now
713 //              printf("%s\r\npara1 = %d, para2 = %d", local_buffer, _para1, _para2);
714 //              fflush(stdout);
715                 return index;
716         }
717 
718 }       // end of namespace gem

bfimain.cpp:

1 #include "bfi.h"
2 
3 int main(int argc, char **argv)
4 {
5         gem::bfi the_app(argc, argv);
6         return the_app.run()? 0: -1;
7 }

Source code can be found in 8up.