jitcraft/Scanner.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jul 01 18:21:15 2012 -0700 (2012-07-01)
changeset 23 8a31742781c1
parent 22 765ab1b672a3
child 25 23e8b3d8e4e6
permissions -rw-r--r--
Implement ++ and -- operators.
     1 /*
     2  * Copyright (C) 2012 David Anderson
     3  *
     4  * This file is part of JITCraft.
     5  *
     6  * JITCraft is free software: you can redistribute it and/or modify it under
     7  * the terms of the GNU General Public License as published by the Free
     8  * Software Foundation, either version 3 of the License, or (at your option)
     9  * any later version.
    10  * 
    11  * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
    12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along with
    16  * JITCraft. If not, see http://www.gnu.org/licenses/.
    17  */
    18 #include <assert.h>
    19 #include <ctype.h>
    20 #include "Scanner.h"
    21 
    22 using namespace jc;
    23 
    24 const char *jc::TokenNames[] =
    25 {
    26 #define _(name, str) str,
    27     TOKENMAP(_)
    28 #undef _
    29     NULL
    30 };
    31 
    32 Scanner::Scanner(const char *stream, size_t length)
    33   : stream_(stream),
    34     length_(length),
    35     pushed_(false),
    36     saved_('\0'),
    37     pos_(stream_)
    38 {
    39 }
    40 
    41 char
    42 Scanner::getChar()
    43 {
    44     if (saved_ != '\0') {
    45         char temp = saved_;
    46         saved_ = '\0';
    47         return temp;
    48     }
    49     if (pos_ >= stream_ + length_)
    50         return '\0';
    51     return *pos_++;
    52 }
    53 
    54 void
    55 Scanner::putChar(char c)
    56 {
    57     assert(saved_ == '\0');
    58     saved_ = c;
    59 }
    60 
    61 void
    62 Scanner::eatSingleLine()
    63 {
    64     char c;
    65     while ((c = getChar()) != '\0') {
    66         if (c == '\n' || c == '\r')
    67             break;
    68     }
    69 }
    70 
    71 bool
    72 Scanner::eatComment()
    73 {
    74     char c;
    75 
    76     while ((c = getChar()) != '\0') {
    77         while (c == '*') {
    78             c = getChar();
    79             if (c == '\0')
    80                 return false;
    81             if (c == '/')
    82                 return true;
    83         }
    84     }
    85 
    86     return false;
    87 }
    88 
    89 static inline bool
    90 IsDigit(char c)
    91 {
    92     return c >= '0' && c <= '9';
    93 }
    94 
    95 // Based off the logic in sc2.c's ftoi()...
    96 double
    97 Scanner::ParseDouble(const char *string)
    98 {
    99     const char *ptr = string;
   100 
   101     double number = 0.0;
   102     while (IsDigit(*ptr)) {
   103         number = (number * 10) + (*ptr - '0');
   104         ptr++;
   105     }
   106 
   107     assert(*ptr == '.');
   108     ptr++;
   109 
   110     double fraction = 0.0;
   111     double multiplier = 1.0;
   112     while (IsDigit(*ptr)) {
   113         fraction = (fraction * 10) + (*ptr - '0');
   114         multiplier = multiplier / 10.0;
   115         ptr++;
   116     }
   117 
   118     number += fraction * multiplier;
   119 
   120     if (*ptr++ == 'e') {
   121         int sign = 1;
   122         if (*ptr == '-') {
   123             sign = -1;
   124             ptr++;
   125         }
   126 
   127         int exponent = 0;
   128         while (IsDigit(*ptr)) {
   129             exponent = (exponent * 10) + (*ptr - '0');
   130             ptr++;
   131         }
   132 
   133         multiplier = pow(10.0, exponent * sign);
   134         number *= multiplier;
   135     }
   136 
   137     return number;
   138 }
   139 
   140 static inline bool
   141 IsHexDigit(char c)
   142 {
   143     return IsDigit(c) ||
   144            (c >= 'a' && c <= 'f') ||
   145            (c >= 'A' && c <= 'F');
   146 }
   147 
   148 // Based on the logic for litchar() in sc2.c.
   149 int
   150 Scanner::readEscapeCode()
   151 {
   152     char c = getChar();
   153     if (c == '\\')
   154         return c;
   155 
   156     switch (c) {
   157       case 'a':
   158         return '\a';
   159       case 'b':
   160         return '\b';
   161       case 'e':
   162         return 27;          // Apparently \e is non-standard.
   163       case 'f':
   164         return '\f';
   165       case 'n':
   166         return '\n';
   167       case 'r':
   168         return '\r';
   169       case 't':
   170         return '\t';
   171       case '\v':
   172         return '\v';
   173 
   174       case 'x':
   175       {
   176           unsigned digits = 0;
   177           char r = 0;
   178   
   179           c = getChar();
   180           while (IsHexDigit(c) && digits < 2) {
   181               if (IsDigit(c))
   182                   c = (c << 4) + (c - '0');
   183               else
   184                   c = (c << 4) + (tolower(c) - 'a' + 10);
   185               digits++;
   186               c = getChar();
   187            }
   188                 
   189           // Swallow a trailing ';'
   190           if (c != ';')
   191               putChar(c);
   192   
   193           return r;
   194       }
   195 
   196       case '\'':
   197       case '\"':
   198       case '%':
   199         return c;
   200 
   201       default:
   202       {
   203         if (IsDigit(c)) {
   204             // \ddd
   205             char r = 0;
   206             while (IsDigit(c)) {
   207                 r = r * 10 + (c - '0');
   208                 c = getChar();
   209             }
   210 
   211             // Swallow a trailing ;
   212             if (c != ';')
   213                 putChar(c);
   214 
   215             return r;
   216         }
   217         assert(false);
   218         return INT_MAX;
   219       }
   220     }
   221 }
   222 
   223 TokenKind
   224 Scanner::stringLiteral()
   225 {
   226     literal_.clear();
   227 
   228     for (;;) {
   229         char c = getChar();
   230         if (c == '\"')
   231             break;
   232         if (c == '\r' || c == '\n' || c == '\0') {
   233             assert(false);
   234             return TOK_ERROR;
   235         }
   236         if (c == '\\') {
   237             int code = readEscapeCode();
   238             if (code == INT_MAX)
   239                 return TOK_ERROR;
   240             c = char(code);
   241         }
   242         literal_ += c;
   243     }
   244 
   245     return TOK_STRING_LITERAL;
   246 }
   247 
   248 TokenKind
   249 Scanner::numberLiteral(char first)
   250 {
   251     literal_ = first;
   252 
   253     char c;
   254     for (;;) {
   255         c = getChar();
   256         if (!IsDigit(c))
   257             break;
   258         literal_ += c;
   259     }
   260 
   261     if (c != '.') {
   262         putChar(c);
   263         return TOK_INTEGER_LITERAL;
   264     }
   265     literal_ += c;
   266 
   267     c = getChar();
   268     if (!IsDigit(c)) {
   269         assert(false);
   270         return TOK_ERROR;
   271     }
   272     literal_ += c;
   273 
   274     for (;;) {
   275         c = getChar();
   276         if (!IsDigit(c))
   277             break;
   278         literal_ += c;
   279     }
   280 
   281     if (c != 'e') {
   282         putChar(c);
   283     } else {
   284         literal_ += c;
   285         c = getChar();
   286         if (c == '-') {
   287             literal_ += c;
   288             c = getChar();
   289         }
   290         if (!IsDigit(c)) {
   291             assert(false);
   292             return TOK_ERROR;
   293         }
   294         literal_ += c;
   295         for (;;) {
   296             c = getChar();
   297             if (!IsDigit(c)) {
   298                 putChar(c);
   299                 break;
   300             }
   301             literal_ += c;
   302         }
   303     }
   304 
   305     return TOK_FLOAT_LITERAL;
   306 }
   307 
   308 TokenKind
   309 Scanner::identifier(char first)
   310 {
   311     literal_ = first;
   312 
   313     char c;
   314     for (;;) {
   315         c = getChar();
   316         if (!isascii(c) || (!isalnum(c) && c != '_')) {
   317             if (c != ':')
   318                 putChar(c);
   319             break;
   320         }
   321         literal_ += c;
   322     }
   323 
   324     if (literal_ == "new")
   325         return TOK_NEW;
   326     if (literal_ == "for")
   327         return TOK_FOR;
   328     if (literal_ == "return")
   329         return TOK_RETURN;
   330     if (literal_ == "if")
   331         return TOK_IF;
   332     if (literal_ == "else")
   333         return TOK_ELSE;
   334     if (literal_ == "enum")
   335         return TOK_ENUM;
   336     if (literal_ == "while")
   337         return TOK_WHILE;
   338     if (literal_ == "do")
   339         return TOK_DO;
   340     if (literal_ == "break")
   341         return TOK_BREAK;
   342     if (literal_ == "continue")
   343         return TOK_CONTINUE;
   344     return (c == ':') ? TOK_LABEL : TOK_NAME;
   345 }
   346 
   347 TokenKind
   348 Scanner::next()
   349 {
   350     assert(!pushed_);
   351 
   352     for (;;) {
   353         char c = getChar();
   354         switch (c) {
   355           case '/':
   356           {
   357             c = getChar();
   358             if (c == '/') {
   359                 eatSingleLine();
   360                 break;
   361             }
   362             if (c == '*') {
   363                 if (!eatComment())
   364                     return TOK_ERROR;
   365                 break;
   366             }
   367             putChar(c);
   368             return TOK_SLASH;
   369           }
   370 
   371           case ';': return TOK_SEMICOLON;
   372           case '{': return TOK_LBRACE;
   373           case '}': return TOK_RBRACE;
   374           case '(': return TOK_LPAREN;
   375           case ')': return TOK_RPAREN;
   376           case '[': return TOK_LBRACKET;
   377           case ']': return TOK_RBRACKET;
   378 
   379           case '*':
   380             c = getChar();
   381             if (c == '=')
   382                 return TOK_ASSIGN_MUL;
   383             putChar(c);
   384             return TOK_STAR;
   385 
   386           case '+':
   387             c = getChar();
   388             if (c == '=')
   389                 return TOK_ASSIGN_ADD;
   390             if (c == '+')
   391                 return TOK_INCREMENT;
   392             putChar(c);
   393             return TOK_PLUS;
   394 
   395           case '&':
   396             c = getChar();
   397             if (c == '=')
   398                 return TOK_ASSIGN_BITAND;
   399             putChar(c);
   400             return TOK_BITAND;
   401 
   402           case '|':
   403             c = getChar();
   404             if (c == '=')
   405                 return TOK_ASSIGN_BITOR;
   406             putChar(c);
   407             return TOK_BITOR;
   408 
   409           case '^':
   410             c = getChar();
   411             if (c == '=')
   412                 return TOK_ASSIGN_BITXOR;
   413             putChar(c);
   414             return TOK_BITXOR;
   415 
   416           case '-':
   417             c = getChar();
   418             if (c == '=')
   419                 return TOK_ASSIGN_SUB;
   420             if (c == '-')
   421                 return TOK_DECREMENT;
   422             putChar(c);
   423             return TOK_MINUS;
   424 
   425           case ',': return TOK_COMMA;
   426           case '\0': return TOK_EOF;
   427 
   428           case '=':
   429             c = getChar();
   430             if (c == '=')
   431                 return TOK_EQUALS;
   432             putChar(c);
   433             return TOK_ASSIGN;
   434 
   435           case '<':
   436             c = getChar();
   437             if (c == '=')
   438                 return TOK_LE;
   439             if (c == '<') {
   440                 c = getChar();
   441                 if (c == '=')
   442                     return TOK_ASSIGN_SHL;
   443                 return TOK_SHL;
   444             }
   445             putChar(c);
   446             return TOK_LT;
   447 
   448           case '>':
   449             c = getChar();
   450             if (c == '=')
   451                 return TOK_GE;
   452             if (c == '>') {
   453                 c = getChar();
   454                 if (c == '>') {
   455                     c = getChar();
   456                     if (c == '=')
   457                         return TOK_ASSIGN_USHR;
   458                     putChar(c);
   459                     return TOK_USHR;
   460                 }
   461                 return TOK_SHR;
   462             }
   463             putChar(c);
   464             return TOK_GT;
   465 
   466           case ' ':
   467           case '\t':
   468           case '\r':
   469           case '\f':
   470           case '\n':
   471             break;
   472 
   473           case '"':
   474             return stringLiteral();
   475 
   476           default:
   477             if (c >= '0' && c <= '9')
   478                 return numberLiteral(c);
   479             if (c == '_' || (isascii(c) && isalpha(c)))
   480                 return identifier(c);
   481             assert(false);
   482             return TOK_ERROR;
   483         }
   484     }
   485 }
   486 
   487 TokenKind
   488 Scanner::scan()
   489 {
   490     if (pushed_) {
   491         pushed_ = false;
   492         return lastToken_;
   493     }
   494     lastToken_ = next();
   495     return lastToken_;
   496 }
   497 
   498 TokenKind
   499 Scanner::peek()
   500 {
   501     if (pushed_)
   502         return lastToken_;
   503     scan();
   504     pushed_ = true;
   505     return lastToken_;
   506 }