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