jitcraft/Scanner.cpp
author David Anderson <dvander@alliedmods.net>
Thu Jun 28 00:26:55 2012 -0700 (2012-06-28)
changeset 8 069d207a8f2a
parent 4 633176823669
child 9 73781c5a59d7
permissions -rw-r--r--
Added support for bitwise 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 TokenKind
   141 Scanner::numberLiteral(char first)
   142 {
   143     literal_ = first;
   144 
   145     char c;
   146     for (;;) {
   147         c = getChar();
   148         if (!IsDigit(c))
   149             break;
   150         literal_ += c;
   151     }
   152 
   153     if (c != '.') {
   154         putChar(c);
   155         return TOK_INTEGER_LITERAL;
   156     }
   157     literal_ += c;
   158 
   159     c = getChar();
   160     if (!IsDigit(c)) {
   161         assert(false);
   162         return TOK_ERROR;
   163     }
   164     literal_ += c;
   165 
   166     for (;;) {
   167         c = getChar();
   168         if (!IsDigit(c))
   169             break;
   170         literal_ += c;
   171     }
   172 
   173     if (c != 'e') {
   174         putChar(c);
   175     } else {
   176         literal_ += c;
   177         c = getChar();
   178         if (c == '-') {
   179             literal_ += c;
   180             c = getChar();
   181         }
   182         if (!IsDigit(c)) {
   183             assert(false);
   184             return TOK_ERROR;
   185         }
   186         literal_ += c;
   187         for (;;) {
   188             c = getChar();
   189             if (!IsDigit(c)) {
   190                 putChar(c);
   191                 break;
   192             }
   193             literal_ += c;
   194         }
   195     }
   196 
   197     return TOK_FLOAT_LITERAL;
   198 }
   199 
   200 TokenKind
   201 Scanner::identifier(char first)
   202 {
   203     literal_ = first;
   204 
   205     char c;
   206     for (;;) {
   207         c = getChar();
   208         if (!isascii(c) || (!isalnum(c) && c != '_')) {
   209             if (c != ':')
   210                 putChar(c);
   211             break;
   212         }
   213         literal_ += c;
   214     }
   215 
   216     if (literal_ == "new")
   217         return TOK_NEW;
   218     if (literal_ == "for")
   219         return TOK_FOR;
   220     if (literal_ == "return")
   221         return TOK_RETURN;
   222     if (literal_ == "if")
   223         return TOK_IF;
   224     if (literal_ == "else")
   225         return TOK_ELSE;
   226     if (literal_ == "enum")
   227         return TOK_ENUM;
   228     return (c == ':') ? TOK_LABEL : TOK_NAME;
   229 }
   230 
   231 TokenKind
   232 Scanner::next()
   233 {
   234     assert(!pushed_);
   235 
   236     for (;;) {
   237         char c = getChar();
   238         switch (c) {
   239           case '/':
   240           {
   241             c = getChar();
   242             if (c == '/') {
   243                 eatSingleLine();
   244                 break;
   245             }
   246             if (c == '*') {
   247                 if (!eatComment())
   248                     return TOK_ERROR;
   249                 break;
   250             }
   251             putChar(c);
   252             return TOK_SLASH;
   253           }
   254 
   255           case ';': return TOK_SEMICOLON;
   256           case '{': return TOK_LBRACE;
   257           case '}': return TOK_RBRACE;
   258           case '(': return TOK_LPAREN;
   259           case ')': return TOK_RPAREN;
   260           case '[': return TOK_LBRACKET;
   261           case ']': return TOK_RBRACKET;
   262 
   263           case '*':
   264             c = getChar();
   265             if (c == '=')
   266                 return TOK_ASSIGN_MUL;
   267             putChar(c);
   268             return TOK_STAR;
   269 
   270           case '+':
   271             c = getChar();
   272             if (c == '=')
   273                 return TOK_ASSIGN_ADD;
   274             putChar(c);
   275             return TOK_PLUS;
   276 
   277           case '&':
   278             return TOK_BITAND;
   279 
   280           case '|':
   281             return TOK_BITOR;
   282 
   283           case '^':
   284             return TOK_BITXOR;
   285 
   286           case '-': return TOK_MINUS;
   287           case ',': return TOK_COMMA;
   288           case '\0': return TOK_EOF;
   289 
   290           case '=':
   291             c = getChar();
   292             if (c == '=')
   293                 return TOK_EQUALS;
   294             putChar(c);
   295             return TOK_ASSIGN;
   296 
   297           case '<':
   298             c = getChar();
   299             if (c == '=')
   300                 return TOK_LE;
   301             if (c == '<') {
   302                 c = getChar();
   303                 if (c == '=')
   304                     return TOK_ASSIGN_SHL;
   305                 return TOK_SHL;
   306             }
   307             putChar(c);
   308             return TOK_LT;
   309 
   310           case '>':
   311             c = getChar();
   312             if (c == '=')
   313                 return TOK_GE;
   314             if (c == '>') {
   315                 c = getChar();
   316                 if (c == '>')
   317                     return TOK_USHR;
   318                 return TOK_SHR;
   319             }
   320             putChar(c);
   321             return TOK_GT;
   322 
   323           case ' ':
   324           case '\t':
   325           case '\r':
   326           case '\f':
   327           case '\n':
   328             break;
   329 
   330           default:
   331             if (c >= '0' && c <= '9')
   332                 return numberLiteral(c);
   333             if (c == '_' || (isascii(c) && isalpha(c)))
   334                 return identifier(c);
   335             assert(false);
   336             return TOK_ERROR;
   337         }
   338     }
   339 }
   340 
   341 TokenKind
   342 Scanner::scan()
   343 {
   344     if (pushed_) {
   345         pushed_ = false;
   346         return lastToken_;
   347     }
   348     lastToken_ = next();
   349     return lastToken_;
   350 }
   351 
   352 TokenKind
   353 Scanner::peek()
   354 {
   355     if (pushed_)
   356         return lastToken_;
   357     scan();
   358     pushed_ = true;
   359     return lastToken_;
   360 }