Canola
0.8.D001
|
00001 // 00002 // canola - canon canola 1614p emulator 00003 // Copyright (C) 2011, 2012 Peter Miller 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License, version 3, as 00007 // published by the Free Software Foundation. 00008 // 00009 // This program is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License along 00015 // with this program. If not, see <http://www.gnu.org/licenses/>. 00016 // 00017 00018 #include <lib/ac/string.h> 00019 #include <map> 00020 00021 #include <lib/calculator.h> 00022 #include <lib/lexer.h> 00023 #include <lib/lexer/functor/method_bool.h> 00024 #include <lib/lexer/functor/opcode.h> 00025 #include <lib/lexer/functor/program_mode.h> 00026 #include <lib/sizeof.h> 00027 #include <lib/unicode.h> 00028 00029 00030 search_path lexer::include_search_path("program-card-package", true); 00031 00032 00033 lexer::~lexer() 00034 { 00035 close(); 00036 } 00037 00038 00039 lexer::lexer(const std::string &filename, calculator &a_subject) : 00040 subject(a_subject), 00041 test_mode(false), 00042 ok(true) 00043 { 00044 src.push_back(source::create(filename, subject)); 00045 } 00046 00047 00048 void 00049 lexer::close(void) 00050 { 00051 src.clear(); 00052 } 00053 00054 00055 void 00056 lexer::open_include_file(const std::string &filename) 00057 { 00058 src.push_back(source::create(filename, subject)); 00059 card_properties_active = false; 00060 } 00061 00062 00063 int 00064 lexer::getch(void) 00065 { 00066 for (;;) 00067 { 00068 if (src.empty()) 00069 return EOF; 00070 int c = src.back()->getch(); 00071 if (c != EOF) 00072 return c; 00073 src.pop_back(); 00074 card_properties_active = false; 00075 } 00076 } 00077 00078 00079 void 00080 lexer::ungetch(int c) 00081 { 00082 if (!src.empty()) 00083 src.back()->ungetch(c); 00084 } 00085 00086 00087 bool 00088 lexer::next_is(char want) 00089 { 00090 int c = getch(); 00091 if (c == EOF) 00092 return false; 00093 if (c == want) 00094 return true; 00095 ungetch(c); 00096 return false; 00097 } 00098 00099 00100 bool 00101 lexer::next_is(const char *want) 00102 { 00103 const char *cp = want; 00104 for (;;) 00105 { 00106 int want_c = (unsigned char)*cp; 00107 if (!want_c) 00108 return true; 00109 int c = getch(); 00110 if (c != want_c) 00111 { 00112 ungetch(c); 00113 while (cp > want) 00114 ungetch(*--cp); 00115 return false; 00116 } 00117 ++cp; 00118 } 00119 } 00120 00121 00122 static std::string 00123 downcase(const std::string &text) 00124 { 00125 std::string result; 00126 for (const char *cp = text.c_str(); ; ) 00127 { 00128 unsigned char c = *cp++; 00129 if (!c) 00130 break; 00131 if (isupper(c)) 00132 c = tolower(c); 00133 result += c; 00134 } 00135 return result; 00136 } 00137 00138 00139 typedef std::map<std::string, lexer_functor::pointer> names_t; 00140 static names_t names; 00141 00142 00143 void 00144 lexer::load_names(void) 00145 { 00146 if (!names.empty()) 00147 return; 00148 00149 struct table_t 00150 { 00151 const char *name; 00152 opcode_t value; 00153 }; 00154 00155 static const table_t table[] = 00156 { 00157 { "bs", opcode_right }, 00158 { "c", opcode_extended_c }, 00159 { "call", opcode_suj }, 00160 { "cancel", opcode_extended_c }, 00161 { "cancel_entry", opcode_clear_indicator }, 00162 { "ce", opcode_clear_indicator }, 00163 { "ceil", opcode_round_up }, 00164 { "ceiling", opcode_round_up }, 00165 { "change_sign", opcode_chg_sign }, 00166 { "chg_sign", opcode_chg_sign }, 00167 { "ci", opcode_clear_indicator }, 00168 { "clear", opcode_extended_c }, 00169 { "clear_display", opcode_clear_indicator }, 00170 { "clear_entry", opcode_clear_indicator }, 00171 { "clear_indicator", opcode_clear_indicator }, 00172 { "cm3_tilde", opcode_extended_cm3_tilde }, 00173 { "cm_tilde", opcode_extended_cm3_tilde }, 00174 { "cs", opcode_chg_sign }, 00175 { "decimal", opcode_dot }, 00176 { "decimal_point", opcode_dot }, 00177 { "display_print", opcode_extended_display_print }, 00178 { "div", opcode_div }, 00179 { "divide", opcode_div }, 00180 { "division", opcode_div }, 00181 { "dot", opcode_dot }, 00182 { "down", opcode_round_down }, 00183 { "e", opcode_ent }, 00184 { "enter", opcode_ent }, 00185 { "enter_jump", opcode_ej }, 00186 { "entry", opcode_ent }, 00187 { "entry_jump", opcode_ej }, 00188 { "exit", opcode_extended_off }, 00189 { "feed", opcode_fd }, 00190 { "flag", opcode_fj }, 00191 { "flag_jump", opcode_fj }, 00192 { "floor", opcode_round_down }, 00193 { "full_stop", opcode_dot }, 00194 { "gosub", opcode_suj }, 00195 { "jmp", opcode_uj }, 00196 { "jsr", opcode_suj }, 00197 { "jump", opcode_uj }, 00198 { "label", opcode_fj }, 00199 { "m3_tilde", opcode_extended_m3_tilde }, 00200 { "m_tilde", opcode_extended_m3_tilde }, 00201 { "minus_equals", opcode_minus_equals }, 00202 { "minus_jump", opcode_mj }, 00203 { "mul", opcode_mul }, 00204 { "multiplication", opcode_mul }, 00205 { "multiply", opcode_mul }, 00206 { "off", opcode_extended_off }, 00207 { "paper_feed", opcode_fd }, 00208 { "period", opcode_dot }, 00209 { "plus_equals", opcode_plus_equals }, 00210 { "power_off", opcode_extended_off }, 00211 { "print_display", opcode_extended_display_print }, 00212 { "print_feed", opcode_fd }, 00213 { "print_program", opcode_extended_program_print }, 00214 { "printer_feed", opcode_fd }, 00215 { "program_print", opcode_extended_program_print }, 00216 { "quit", opcode_extended_off }, 00217 { "return", opcode_srj }, 00218 { "rev", opcode_rv }, 00219 { "reverse", opcode_rv }, 00220 { "reverse_values", opcode_rv }, 00221 { "right", opcode_right }, 00222 { "right_shift", opcode_right }, 00223 { "rm3_tilde", opcode_extended_rm3_tilde }, 00224 { "rm_tilde", opcode_extended_rm3_tilde }, 00225 { "root", opcode_sqrt }, 00226 { "round", opcode_round_off }, 00227 { "rts", opcode_srj }, 00228 { "s", opcode_ent }, 00229 { "sc", opcode_chg_sign }, 00230 { "sense_jump", opcode_sj }, 00231 { "shift", opcode_right }, 00232 { "shift_right", opcode_right }, 00233 { "sign_change", opcode_chg_sign }, 00234 { "sign_chg", opcode_chg_sign }, 00235 { "sm3_tilde", opcode_extended_sm3_tilde }, 00236 { "sm_tilde", opcode_extended_sm3_tilde }, 00237 { "sqrt", opcode_sqrt }, 00238 { "square_root", opcode_sqrt }, 00239 { "start", opcode_extended_start }, 00240 { "stop", opcode_ent }, 00241 { "subroutine_flag", opcode_sfj }, 00242 { "subroutine_flag_jump", opcode_sfj }, 00243 { "subroutine_jump", opcode_suj }, 00244 { "subroutine_label", opcode_sfj }, 00245 { "subroutine_return_jump", opcode_srj }, 00246 { "times", opcode_mul }, 00247 { "unconditional_jump", opcode_uj }, 00248 { "up", opcode_round_up }, 00249 { "x", opcode_mul }, 00250 }; 00251 00252 // Insert the table values into the map. 00253 // This will be faster than a linear search. 00254 for (const table_t *tp = table; tp < ENDOF(table); ++tp) 00255 { 00256 names[tp->name] = lexer_functor_opcode::create(subject, tp->value); 00257 } 00258 00259 if (test_mode) 00260 { 00261 static const table_t table2[] = 00262 { 00263 { "rounding_switch_up", opcode_extended_rounding_switch_up }, 00264 { "rounding_switch_off", opcode_extended_rounding_switch_off }, 00265 { "rounding_switch_down", opcode_extended_rounding_switch_down }, 00266 { "decimal_point_selector_0", 00267 opcode_extended_decimal_point_selector_0 }, 00268 { "decimal_point_selector_1", 00269 opcode_extended_decimal_point_selector_1 }, 00270 { "decimal_point_selector_2", 00271 opcode_extended_decimal_point_selector_2 }, 00272 { "decimal_point_selector_3", 00273 opcode_extended_decimal_point_selector_3 }, 00274 { "decimal_point_selector_4", 00275 opcode_extended_decimal_point_selector_4 }, 00276 { "decimal_point_selector_5", 00277 opcode_extended_decimal_point_selector_5 }, 00278 { "decimal_point_selector_6", 00279 opcode_extended_decimal_point_selector_6 }, 00280 { "decimal_point_selector_7", 00281 opcode_extended_decimal_point_selector_7 }, 00282 { "decimal_point_selector_8", 00283 opcode_extended_decimal_point_selector_8 }, 00284 { "decimal_point_selector_9", 00285 opcode_extended_decimal_point_selector_9 }, 00286 { "decimal_point_selector_10", 00287 opcode_extended_decimal_point_selector_10 }, 00288 { "decimal_point_selector_11", 00289 opcode_extended_decimal_point_selector_11 }, 00290 { "decimal_point_selector_12", 00291 opcode_extended_decimal_point_selector_12 }, 00292 { "decimal_point_selector_float", 00293 opcode_extended_decimal_point_selector_float }, 00294 }; 00295 00296 for (const table_t *tp = table2; tp < ENDOF(table2); ++tp) 00297 { 00298 names[tp->name] = lexer_functor_opcode::create(subject, tp->value); 00299 } 00300 00301 { 00302 lexer_functor::pointer lrn = 00303 lexer_functor_program_mode::create 00304 ( 00305 subject, 00306 calculator::program_mode_learn 00307 ); 00308 names["lrn"] = lrn; 00309 names["learn"] = lrn; 00310 } 00311 00312 { 00313 lexer_functor::pointer ope = 00314 lexer_functor_program_mode::create 00315 ( 00316 subject, 00317 calculator::program_mode_operation 00318 ); 00319 names["ope"] = ope; 00320 names["operation"] = ope; 00321 names["operate"] = ope; 00322 } 00323 00324 { 00325 lexer_functor::pointer pro_che = 00326 lexer_functor_program_mode::create 00327 ( 00328 subject, 00329 calculator::program_mode_program_check 00330 ); 00331 names["pro_che"] = pro_che; 00332 names["program_check"] = pro_che; 00333 } 00334 00335 { 00336 lexer_functor::pointer ope_che = 00337 lexer_functor_program_mode::create 00338 ( 00339 subject, 00340 calculator::program_mode_operation_check 00341 ); 00342 names["ope_che"] = ope_che; 00343 names["operate_check"] = ope_che; 00344 names["operation_check"] = ope_che; 00345 } 00346 00347 { 00348 lexer_functor::pointer on = 00349 lexer_functor_method_bool::create 00350 ( 00351 subject, 00352 &calculator::accumulate_mode_set, 00353 true 00354 ); 00355 names["am1_down"] = on; 00356 names["am1_lock"] = on; 00357 names["am1_on"] = on; 00358 } 00359 00360 { 00361 lexer_functor::pointer off = 00362 lexer_functor_method_bool::create 00363 ( 00364 subject, 00365 &calculator::accumulate_mode_set, 00366 false 00367 ); 00368 names["am1_off"] = off; 00369 names["am1_unlock"] = off; 00370 names["am1_up"] = off; 00371 } 00372 00373 { 00374 lexer_functor::pointer on = 00375 lexer_functor_method_bool::create 00376 ( 00377 subject, 00378 &calculator::constant_mode_set, 00379 true 00380 ); 00381 names["const_on"] = on; 00382 names["constant_on"] = on; 00383 names["k_down"] = on; 00384 names["k_lock"] = on; 00385 names["k_on"] = on; 00386 names["konstant_on"] = on; 00387 } 00388 00389 { 00390 lexer_functor::pointer off = 00391 lexer_functor_method_bool::create 00392 ( 00393 subject, 00394 &calculator::constant_mode_set, 00395 false 00396 ); 00397 names["const_off"] = off; 00398 names["constant_off"] = off; 00399 names["k_off"] = off; 00400 names["k_unlock"] = off; 00401 names["k_up"] = off; 00402 names["konstant_off"] = off; 00403 } 00404 } 00405 00406 // If the tables contain conflicts, the real opcodes win. 00407 for (int j = 1; j < 127; ++j) 00408 { 00409 if (opcode_valid(j)) 00410 { 00411 names[downcase(opcode_name((opcode_t)j))] = 00412 lexer_functor_opcode::create(subject, (opcode_t)j); 00413 } 00414 } 00415 } 00416 00417 00418 struct multibyte_t 00419 { 00420 const char *name; 00421 opcode_t value; 00422 }; 00423 00424 00425 static const multibyte_t multibyte[] = 00426 { 00427 { "5/4", opcode_round_off }, 00428 { UNICODE_MUL, opcode_mul }, 00429 { UNICODE_DIV, opcode_div }, 00430 { UNICODE_UP, opcode_round_up }, 00431 { UNICODE_RIGHT, opcode_right }, 00432 { UNICODE_DOWN, opcode_round_down }, 00433 { UNICODE_SQRT, opcode_sqrt }, 00434 { UNICODE_PRINT, opcode_print }, 00435 { UNICODE_MINUS "=", opcode_minus_equals }, 00436 }; 00437 00438 00439 static std::string 00440 trim(const std::string &text) 00441 { 00442 std::string result; 00443 const char *s = text.c_str(); 00444 bool white_space = false; 00445 for (;;) 00446 { 00447 unsigned char c = *s++; 00448 switch (c) 00449 { 00450 case '\0': 00451 return result; 00452 00453 case ' ': 00454 case '\f': 00455 case '\n': 00456 case '\r': 00457 case '\t': 00458 case '\v': 00459 if (result.size()) 00460 white_space = true; 00461 break; 00462 00463 default: 00464 if (white_space) 00465 { 00466 result += ' '; 00467 white_space = false; 00468 } 00469 result += c; 00470 break; 00471 } 00472 } 00473 } 00474 00475 00476 void 00477 lexer::process_comment(const std::string &text) 00478 { 00479 if (src.size() != 1) 00480 return; 00481 if (card_properties_active) 00482 { 00483 if (strstr(text.c_str(), "Card-Pocket-End")) 00484 { 00485 card_properties_active = false; 00486 } 00487 else 00488 { 00489 const char *s = text.c_str(); 00490 const char *colon = strchr(s, ':'); 00491 if (colon) 00492 { 00493 std::string name = trim(std::string(s, colon - s)); 00494 std::string value = trim(std::string(colon + 1)); 00495 subject.property(name, value); 00496 } 00497 } 00498 } 00499 else 00500 { 00501 card_properties_active = !!strstr(text.c_str(), "Card-Pocket-Begin"); 00502 } 00503 } 00504 00505 00506 bool 00507 lexer::read_and_execute(void) 00508 { 00509 ok = true; 00510 load_names(); 00511 for (;;) 00512 { 00513 location::pointer start_loc = get_location(); 00514 00515 // First do some read-ahead, looking for multi-byte special cases 00516 for (const multibyte_t *mb = multibyte; mb < ENDOF(multibyte); ++mb) 00517 { 00518 if (next_is(mb->name)) 00519 { 00520 subject.on_opcode(mb->value, get_location(start_loc)); 00521 continue; 00522 } 00523 } 00524 00525 int c = getch(); 00526 switch (c) 00527 { 00528 case EOF: 00529 return ok; 00530 00531 case '\n': 00532 case '\t': 00533 case '\f': 00534 case '\r': 00535 case ' ': 00536 // Ignore white space, 00537 // except as it serves to separate tokens. 00538 break; 00539 00540 case '#': 00541 comment: 00542 { 00543 std::string text; 00544 for (;;) 00545 { 00546 int c = getch(); 00547 if (c == EOF || c == '\n') 00548 { 00549 ungetch(c); 00550 break; 00551 } 00552 text += (char)c; 00553 } 00554 process_comment(text); 00555 } 00556 break; 00557 00558 case '$': 00559 { 00560 // This is a magical escape so that we can insert 00561 // arbitrary values into the code stream. 00562 int c1 = getch(); 00563 if (c1 < '0' || c > '7') 00564 { 00565 dollar_barf: 00566 error 00567 ( 00568 "the $ symbol must be followed by exactly three " 00569 "digits, and form a valid code" 00570 ); 00571 close(); 00572 return false; 00573 } 00574 c1 -= '0'; 00575 00576 int c2 = getch(); 00577 if (c2 < '0' || c2 > '1') 00578 goto dollar_barf; 00579 c2 -= '0'; 00580 00581 int c3 = getch(); 00582 if (c3 < '0' || c > '9') 00583 goto dollar_barf; 00584 c3 -= '0'; 00585 00586 int code = (c1 << 4) + (c2 * 10) + c3; 00587 if (code == 0 || code == 0x7F) 00588 goto dollar_barf; 00589 subject.on_opcode((opcode_t)code, get_location(start_loc)); 00590 } 00591 break; 00592 00593 case '*': 00594 subject.on_opcode(opcode_mul, get_location(start_loc)); 00595 break; 00596 00597 case '+': 00598 if (next_is('=')) 00599 { 00600 subject.on_opcode(opcode_plus_equals, get_location(start_loc)); 00601 break; 00602 } 00603 goto junk; 00604 00605 case '-': 00606 if (next_is('=')) 00607 { 00608 subject.on_opcode(opcode_minus_equals, get_location(start_loc)); 00609 break; 00610 } 00611 if (next_is('>')) 00612 { 00613 subject.on_opcode(opcode_right, get_location(start_loc)); 00614 break; 00615 } 00616 subject.on_opcode(opcode_chg_sign, get_location(start_loc)); 00617 break; 00618 00619 case '.': 00620 subject.on_opcode(opcode_dot, get_location(start_loc)); 00621 break; 00622 00623 case '/': 00624 subject.on_opcode(opcode_div, get_location(start_loc)); 00625 break; 00626 00627 // FIXME: the "5/4" opcode could be a problem. 00628 00629 case '0': case '1': case '2': case '3': case '4': 00630 case '5': case '6': case '7': case '8': case '9': 00631 subject.on_opcode 00632 ( 00633 opcode_t(opcode_n0 + c - '0'), 00634 get_location(start_loc) 00635 ); 00636 break; 00637 00638 case ';': 00639 goto comment; 00640 00641 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 00642 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 00643 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 00644 case 'V': case 'W': case 'X': case 'Y': case 'Z': 00645 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 00646 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 00647 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 00648 case 'v': case 'w': case 'x': case 'y': case 'z': 00649 { 00650 std::string name; 00651 for (;;) 00652 { 00653 if (isupper((unsigned char)c)) 00654 c = tolower((unsigned char)c); 00655 name += (char)c; 00656 c = getch(); 00657 switch (c) 00658 { 00659 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 00660 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 00661 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 00662 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 00663 case 'Y': case 'Z': 00664 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 00665 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 00666 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 00667 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 00668 case 'y': case 'z': 00669 case '0': case '1': case '2': case '3': case '4': 00670 case '5': case '6': case '7': case '8': case '9': 00671 case '_': case '~': 00672 continue; 00673 00674 default: 00675 ungetch(c); 00676 break; 00677 } 00678 break; 00679 } 00680 00681 // opcodes 00682 names_t::const_iterator it = names.find(name); 00683 if (it != names.end()) 00684 { 00685 (*(it->second))(get_location(start_loc)); 00686 break; 00687 } 00688 00689 if (name == "include") 00690 { 00691 // rest of line is include file name 00692 std::string filename; 00693 for (;;) 00694 { 00695 c = getch(); 00696 switch (c) 00697 { 00698 default: 00699 filename += (char)c; 00700 continue; 00701 00702 case '\t': 00703 case ' ': 00704 if (filename.empty()) 00705 continue; 00706 ungetch(c); 00707 break; 00708 00709 case '\n': 00710 case '\r': 00711 case '#': 00712 case ';': 00713 ungetch(c); 00714 break; 00715 00716 case EOF: 00717 break; 00718 } 00719 break; 00720 } 00721 open_include_file(filename); 00722 break; 00723 } 00724 00725 // no idea 00726 error("unknown key name \"%s\"", name.c_str()); 00727 close(); 00728 return false; 00729 } 00730 00731 default: 00732 junk: 00733 if (isgraph((unsigned char)c)) 00734 error("invalid input, near '%c'", c); 00735 else 00736 error("invalid input"); 00737 close(); 00738 return false; 00739 } 00740 } 00741 } 00742 00743 00744 bool 00745 lexer::read_binary_and_execute(void) 00746 { 00747 ok = true; 00748 int address = 0; 00749 for (;;) 00750 { 00751 // Stupid, I know, but the 1614P used 1-based addresses, instead 00752 // of the more modern 0-based addresses. This *was* only 1972, 00753 // and the 1614P was probably designed a couple of years before 00754 // that, so we can cut them *some* slack. 00755 ++address; 00756 00757 int c = getch(); 00758 switch (c) 00759 { 00760 case EOF: 00761 return ok; 00762 00763 case 0x00: 00764 case 0x7F: 00765 // These are ignored by the 1614P's card reader. 00766 break; 00767 00768 default: 00769 if (c >= 1 && c <= 126) 00770 { 00771 // Allow almost anything here. This is because random 00772 // shite can be used for flag jump labels. 00773 subject.on_opcode(opcode_t(c), location::pointer()); 00774 break; 00775 } 00776 00777 // Oops. 00778 src.back()->set_line_number(address); 00779 error("invalid code 0x%02X", c); 00780 return false; 00781 } 00782 } 00783 } 00784 00785 00786 void 00787 lexer::error(const char *fmt, ...) 00788 { 00789 va_list ap; 00790 va_start(ap, fmt); 00791 if (src.empty()) 00792 subject.on_error_v(fmt, ap); 00793 else 00794 src.back()->error_v(fmt, ap); 00795 va_end(ap); 00796 00797 // Finish processing after the first error, because we could be 00798 // feeding the GUI calculator input at this point. 00799 close(); 00800 00801 ok = false; 00802 } 00803 00804 00805 void 00806 lexer::search_path_append(const std::string &dir) 00807 { 00808 include_search_path.append(dir); 00809 } 00810 00811 00812 void 00813 lexer::test_mode_set(bool value) 00814 { 00815 test_mode = value; 00816 } 00817 00818 00819 location::pointer 00820 lexer::get_location(void) 00821 const 00822 { 00823 if (src.empty()) 00824 return location::pointer(); 00825 return src.back()->get_location(); 00826 } 00827 00828 00829 location::pointer 00830 lexer::get_location(const location::pointer &start) 00831 const 00832 { 00833 location::pointer end = get_location(); 00834 return location::span(start, end); 00835 }