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.