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