Canola  0.8.D001
lib/lexer.cc
Go to the documentation of this file.
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 }