Canola  0.8.D001
test_number/lex.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/stdio.h>
00019 #include <libexplain/getchar.h>
00020 #include <libexplain/output.h>
00021 #include <libexplain/ungetc.h>
00022 
00023 #include <lib/number.h>
00024 
00025 #include <test_number/lex.h>
00026 #include <test_number/grammar.yacc.h>
00027 
00028 
00029 static int number_of_errors;
00030 static int line_number = 1;
00031 static bool prev_was_eoln;
00032 
00033 
00034 void
00035 lex_end(void)
00036 {
00037     if (number_of_errors > 0)
00038     {
00039         explain_output_error_and_die
00040         (
00041             "found %d error%s",
00042             number_of_errors,
00043             (number_of_errors == 1 ? "" : "s")
00044         );
00045     }
00046 }
00047 
00048 
00049 static bool
00050 have(char c)
00051 {
00052     int t = explain_getchar_or_die();
00053     if (t == EOF)
00054         return false;
00055     if (t == c)
00056         return true;
00057     explain_ungetc_or_die(t, stdin);
00058     return false;
00059 }
00060 
00061 
00062 int
00063 grammar_lex(void)
00064 {
00065     if (prev_was_eoln)
00066         ++line_number;
00067     for (;;)
00068     {
00069         int c = explain_getchar_or_die();
00070         switch (c)
00071         {
00072         case EOF:
00073             return 0;
00074 
00075         case '\t':
00076             break;
00077 
00078         case '\n':
00079             prev_was_eoln = true;
00080             return EOLN;
00081 
00082         case ' ':
00083             break;
00084 
00085         case '!':
00086             if (have('='))
00087                 return NE;
00088             return JUNK;
00089 
00090         case '(':
00091             return LP;
00092 
00093         case ')':
00094             return RP;
00095 
00096         case '*':
00097             return MUL;
00098 
00099         case '+':
00100             return PLUS;
00101 
00102         case ',':
00103             return COMMA;
00104 
00105         case '-':
00106             return MINUS;
00107 
00108         case '/':
00109             return DIV;
00110 
00111         case '0': case '1': case '2': case '3': case '4':
00112         case '5': case '6': case '7': case '8': case '9':
00113             {
00114                 number n;
00115                 bool dotted = false;
00116                 for (;;)
00117                 {
00118                     if (dotted)
00119                         n.insert_digit_after_dot(c - '0');
00120                     else
00121                         n.insert_digit_before_dot(c - '0');
00122                     get_more:
00123                     c = explain_getchar_or_die();
00124                     switch (c)
00125                     {
00126                     case EOF:
00127                         break;
00128 
00129                     case '.':
00130                         dotted = true;
00131                         n.insert_dot();
00132                         goto get_more;
00133 
00134                     case '0': case '1': case '2': case '3': case '4':
00135                     case '5': case '6': case '7': case '8': case '9':
00136                         continue;
00137 
00138                     default:
00139                         explain_ungetc_or_die(c, stdin);
00140                         break;
00141                     }
00142                     break;
00143                 }
00144                 grammar_lval.lv_number = new number(n);
00145             }
00146             return NUMBER;
00147 
00148         case '<':
00149             if (have('='))
00150                 return LE;
00151             return LT;
00152 
00153         case '=':
00154             if (have('='))
00155                 return EQ;
00156             return JUNK;
00157 
00158         case '>':
00159             if (have('='))
00160                 return GE;
00161             return GT;
00162 
00163         case '_':
00164         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
00165         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
00166         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
00167         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
00168         case 'Y': case 'Z':
00169         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
00170         case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
00171         case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
00172         case 's': case 't': case 'u': case 'v': case 'w': case 'x':
00173         case 'y': case 'z':
00174             {
00175                 std::string name;
00176                 for (;;)
00177                 {
00178                     name += (char)c;
00179 
00180                     c = explain_getchar_or_die();
00181                     switch (c)
00182                     {
00183                     case EOF:
00184                         break;
00185 
00186                     case '0': case '1': case '2': case '3': case '4':
00187                     case '5': case '6': case '7': case '8': case '9':
00188                     case '_':
00189                     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
00190                     case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
00191                     case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
00192                     case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
00193                     case 'Y': case 'Z':
00194                     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
00195                     case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
00196                     case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
00197                     case 's': case 't': case 'u': case 'v': case 'w': case 'x':
00198                     case 'y': case 'z':
00199                         continue;
00200 
00201                     default:
00202                         explain_ungetc_or_die(c, stdin);
00203                         break;
00204                     }
00205                     break;
00206                 }
00207                 if (name == "sqrt")
00208                     return SQRT;
00209                 if (name == "round_down")
00210                     return FLOOR;
00211             }
00212             return JUNK;
00213 
00214         default:
00215             return JUNK;
00216         }
00217     }
00218 }
00219 
00220 
00221 void
00222 grammar_error(const char *text)
00223 {
00224     explain_output_error("%d: %s", line_number, text);
00225     ++number_of_errors;
00226 }
00227 
00228 
00229 // vim: set ts=8 sw=4 et :