jitcraft/Scanner.cpp
author David Anderson <dvander@alliedmods.net>
Wed Jul 18 21:46:29 2012 -0700 (2012-07-18)
changeset 82 f99bc938522a
parent 78 93e0a19c805a
child 83 79265b56316e
permissions -rw-r--r--
Added support for modulo operator.
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@59
    24
using namespace ke;
dvander@0
    25
dvander@59
    26
const char *ke::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@71
    43
    cur_.col = 1;
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@71
    73
    cur_.col = 1;
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@63
   368
    if (literal_ == "forward")
dvander@63
   369
        return TOK_FORWARD;
dvander@63
   370
    if (literal_ == "public")
dvander@63
   371
        return TOK_PUBLIC;
dvander@67
   372
    if (literal_ == "const")
dvander@67
   373
        return TOK_CONST;
dvander@68
   374
    if (literal_ == "native")
dvander@68
   375
        return TOK_NATIVE;
dvander@4
   376
    return (c == ':') ? TOK_LABEL : TOK_NAME;
dvander@0
   377
}
dvander@0
   378
dvander@0
   379
TokenKind
dvander@0
   380
Scanner::next()
dvander@0
   381
{
dvander@0
   382
    assert(!pushed_);
dvander@0
   383
dvander@0
   384
    for (;;) {
dvander@25
   385
        begin_ = cur_;
dvander@25
   386
dvander@0
   387
        char c = getChar();
dvander@0
   388
        switch (c) {
dvander@0
   389
          case '/':
dvander@0
   390
          {
dvander@0
   391
            c = getChar();
dvander@0
   392
            if (c == '/') {
dvander@0
   393
                eatSingleLine();
dvander@0
   394
                break;
dvander@0
   395
            }
dvander@0
   396
            if (c == '*') {
dvander@25
   397
                if (!eatComment()) {
dvander@25
   398
                    cc_.reportError(begin(), Message_UnterminatedComment);
dvander@0
   399
                    return TOK_ERROR;
dvander@25
   400
                }
dvander@0
   401
                break;
dvander@0
   402
            }
dvander@0
   403
            putChar(c);
dvander@0
   404
            return TOK_SLASH;
dvander@0
   405
          }
dvander@0
   406
dvander@25
   407
          case '\n':
dvander@25
   408
          case '\r':
dvander@25
   409
            calcNewLine(c);
dvander@25
   410
            break;
dvander@25
   411
dvander@0
   412
          case ';': return TOK_SEMICOLON;
dvander@0
   413
          case '{': return TOK_LBRACE;
dvander@0
   414
          case '}': return TOK_RBRACE;
dvander@0
   415
          case '(': return TOK_LPAREN;
dvander@0
   416
          case ')': return TOK_RPAREN;
dvander@0
   417
          case '[': return TOK_LBRACKET;
dvander@0
   418
          case ']': return TOK_RBRACKET;
dvander@78
   419
          case '~': return TOK_TILDE;
dvander@2
   420
dvander@68
   421
          case '.':
dvander@68
   422
            c = getChar();
dvander@68
   423
            if (c == '.') {
dvander@68
   424
                c = getChar();
dvander@68
   425
                if (c == '.')
dvander@68
   426
                    return TOK_ELLIPSES;
dvander@68
   427
            }
dvander@68
   428
            cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c));
dvander@68
   429
            return TOK_ERROR;
dvander@68
   430
dvander@2
   431
          case '*':
dvander@2
   432
            c = getChar();
dvander@2
   433
            if (c == '=')
dvander@2
   434
                return TOK_ASSIGN_MUL;
dvander@2
   435
            putChar(c);
dvander@2
   436
            return TOK_STAR;
dvander@2
   437
dvander@2
   438
          case '+':
dvander@2
   439
            c = getChar();
dvander@2
   440
            if (c == '=')
dvander@2
   441
                return TOK_ASSIGN_ADD;
dvander@23
   442
            if (c == '+')
dvander@23
   443
                return TOK_INCREMENT;
dvander@2
   444
            putChar(c);
dvander@2
   445
            return TOK_PLUS;
dvander@2
   446
dvander@8
   447
          case '&':
dvander@22
   448
            c = getChar();
dvander@22
   449
            if (c == '=')
dvander@22
   450
                return TOK_ASSIGN_BITAND;
dvander@75
   451
            if (c == '&')
dvander@75
   452
                return TOK_AND;
dvander@22
   453
            putChar(c);
dvander@8
   454
            return TOK_BITAND;
dvander@8
   455
dvander@8
   456
          case '|':
dvander@22
   457
            c = getChar();
dvander@22
   458
            if (c == '=')
dvander@22
   459
                return TOK_ASSIGN_BITOR;
dvander@75
   460
            if (c == '|')
dvander@75
   461
                return TOK_OR;
dvander@22
   462
            putChar(c);
dvander@8
   463
            return TOK_BITOR;
dvander@8
   464
dvander@8
   465
          case '^':
dvander@22
   466
            c = getChar();
dvander@22
   467
            if (c == '=')
dvander@22
   468
                return TOK_ASSIGN_BITXOR;
dvander@22
   469
            putChar(c);
dvander@8
   470
            return TOK_BITXOR;
dvander@8
   471
dvander@82
   472
          case '%':
dvander@82
   473
            c = getChar();
dvander@82
   474
            if (c == '=')
dvander@82
   475
                return TOK_ASSIGN_MOD;
dvander@82
   476
            putChar(c);
dvander@82
   477
            return TOK_PERCENT;
dvander@82
   478
dvander@22
   479
          case '-':
dvander@22
   480
            c = getChar();
dvander@22
   481
            if (c == '=')
dvander@22
   482
                return TOK_ASSIGN_SUB;
dvander@23
   483
            if (c == '-')
dvander@23
   484
                return TOK_DECREMENT;
dvander@22
   485
            putChar(c);
dvander@22
   486
            return TOK_MINUS;
dvander@22
   487
dvander@0
   488
          case ',': return TOK_COMMA;
dvander@0
   489
          case '\0': return TOK_EOF;
dvander@0
   490
dvander@74
   491
          case '!':
dvander@74
   492
            c = getChar();
dvander@74
   493
            if (c == '=')
dvander@74
   494
                return TOK_NOTEQUALS;
dvander@74
   495
            putChar(c);
dvander@74
   496
            return TOK_NOT;
dvander@74
   497
dvander@0
   498
          case '=':
dvander@0
   499
            c = getChar();
dvander@0
   500
            if (c == '=')
dvander@0
   501
                return TOK_EQUALS;
dvander@0
   502
            putChar(c);
dvander@0
   503
            return TOK_ASSIGN;
dvander@0
   504
dvander@0
   505
          case '<':
dvander@0
   506
            c = getChar();
dvander@0
   507
            if (c == '=')
dvander@0
   508
                return TOK_LE;
dvander@2
   509
            if (c == '<') {
dvander@2
   510
                c = getChar();
dvander@2
   511
                if (c == '=')
dvander@2
   512
                    return TOK_ASSIGN_SHL;
dvander@2
   513
                return TOK_SHL;
dvander@2
   514
            }
dvander@0
   515
            putChar(c);
dvander@0
   516
            return TOK_LT;
dvander@0
   517
dvander@0
   518
          case '>':
dvander@0
   519
            c = getChar();
dvander@0
   520
            if (c == '=')
dvander@0
   521
                return TOK_GE;
dvander@8
   522
            if (c == '>') {
dvander@8
   523
                c = getChar();
dvander@22
   524
                if (c == '>') {
dvander@22
   525
                    c = getChar();
dvander@22
   526
                    if (c == '=')
dvander@22
   527
                        return TOK_ASSIGN_USHR;
dvander@22
   528
                    putChar(c);
dvander@8
   529
                    return TOK_USHR;
dvander@22
   530
                }
dvander@8
   531
                return TOK_SHR;
dvander@8
   532
            }
dvander@0
   533
            putChar(c);
dvander@0
   534
            return TOK_GT;
dvander@0
   535
dvander@0
   536
          case ' ':
dvander@0
   537
          case '\t':
dvander@0
   538
          case '\f':
dvander@0
   539
            break;
dvander@0
   540
dvander@21
   541
          case '"':
dvander@21
   542
            return stringLiteral();
dvander@21
   543
dvander@0
   544
          default:
dvander@0
   545
            if (c >= '0' && c <= '9')
dvander@4
   546
                return numberLiteral(c);
dvander@0
   547
            if (c == '_' || (isascii(c) && isalpha(c)))
dvander@0
   548
                return identifier(c);
dvander@25
   549
            cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c));
dvander@0
   550
            return TOK_ERROR;
dvander@0
   551
        }
dvander@0
   552
    }
dvander@0
   553
}
dvander@0
   554
dvander@0
   555
TokenKind
dvander@0
   556
Scanner::scan()
dvander@0
   557
{
dvander@0
   558
    if (pushed_) {
dvander@0
   559
        pushed_ = false;
dvander@0
   560
        return lastToken_;
dvander@0
   561
    }
dvander@0
   562
    lastToken_ = next();
dvander@0
   563
    return lastToken_;
dvander@0
   564
}
dvander@0
   565
dvander@0
   566
TokenKind
dvander@0
   567
Scanner::peek()
dvander@0
   568
{
dvander@0
   569
    if (pushed_)
dvander@0
   570
        return lastToken_;
dvander@0
   571
    scan();
dvander@0
   572
    pushed_ = true;
dvander@0
   573
    return lastToken_;
dvander@0
   574
}