// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_TOKEN_H_
#define RUNTIME_VM_TOKEN_H_

#include "platform/assert.h"
#include "vm/allocation.h"

namespace dart {

// Token definitions.
// Some operator tokens appear in blocks, e.g. assignment operators.
// There is code that depends on the values within a block to be
// contiguous, and on the order of values.
#define DART_TOKEN_LIST(TOK)                                                   \
  TOK(kEOS, "", 0, kNoAttribute)                                               \
                                                                               \
  TOK(kLPAREN, "(", 0, kNoAttribute)                                           \
  TOK(kRPAREN, ")", 0, kNoAttribute)                                           \
  TOK(kLBRACK, "[", 0, kNoAttribute)                                           \
  TOK(kRBRACK, "]", 0, kNoAttribute)                                           \
  TOK(kLBRACE, "{", 0, kNoAttribute)                                           \
  TOK(kRBRACE, "}", 0, kNoAttribute)                                           \
  TOK(kARROW, "=>", 0, kNoAttribute)                                           \
  TOK(kCOLON, ":", 0, kNoAttribute)                                            \
  TOK(kSEMICOLON, ";", 0, kNoAttribute)                                        \
  TOK(kPERIOD, ".", 0, kNoAttribute)                                           \
  TOK(kQM_PERIOD, "?.", 0, kNoAttribute)                                       \
  TOK(kINCR, "++", 0, kNoAttribute)                                            \
  TOK(kDECR, "--", 0, kNoAttribute)                                            \
                                                                               \
  /* Assignment operators.                            */                       \
  /* Please update IsAssignmentOperator() if you make */                       \
  /* any changes to this block.                       */                       \
  TOK(kASSIGN, "=", 2, kNoAttribute)                                           \
  TOK(kASSIGN_OR, "|=", 2, kNoAttribute)                                       \
  TOK(kASSIGN_XOR, "^=", 2, kNoAttribute)                                      \
  TOK(kASSIGN_AND, "&=", 2, kNoAttribute)                                      \
  TOK(kASSIGN_SHL, "<<=", 2, kNoAttribute)                                     \
  TOK(kASSIGN_SHR, ">>=", 2, kNoAttribute)                                     \
  TOK(kASSIGN_USHR, ">>>=", 2, kNoAttribute)                                   \
  TOK(kASSIGN_ADD, "+=", 2, kNoAttribute)                                      \
  TOK(kASSIGN_SUB, "-=", 2, kNoAttribute)                                      \
  TOK(kASSIGN_MUL, "*=", 2, kNoAttribute)                                      \
  TOK(kASSIGN_TRUNCDIV, "~/=", 2, kNoAttribute)                                \
  TOK(kASSIGN_DIV, "/=", 2, kNoAttribute)                                      \
  TOK(kASSIGN_MOD, "%=", 2, kNoAttribute)                                      \
  /* Avoid trigraph ??= below. */                                              \
  TOK(kASSIGN_COND, "?\?=", 2, kNoAttribute)                                   \
                                                                               \
  TOK(kCASCADE, "..", 2, kNoAttribute)                                         \
                                                                               \
  TOK(kCOMMA, ",", 1, kNoAttribute)                                            \
  TOK(kOR, "||", 5, kNoAttribute)                                              \
  TOK(kAND, "&&", 6, kNoAttribute)                                             \
  TOK(kBIT_OR, "|", 9, kNoAttribute)                                           \
  TOK(kBIT_XOR, "^", 10, kNoAttribute)                                         \
  TOK(kBIT_AND, "&", 11, kNoAttribute)                                         \
  TOK(kBIT_NOT, "~", 0, kNoAttribute)                                          \
                                                                               \
  /* Shift operators. */                                                       \
  TOK(kSHL, "<<", 12, kNoAttribute)                                            \
  TOK(kSHR, ">>", 12, kNoAttribute)                                            \
  TOK(kUSHR, ">>>", 12, kNoAttribute)                                          \
                                                                               \
  /* Additive operators. */                                                    \
  TOK(kADD, "+", 13, kNoAttribute)                                             \
  TOK(kSUB, "-", 13, kNoAttribute)                                             \
                                                                               \
  /* Multiplicative operators */                                               \
  TOK(kMUL, "*", 14, kNoAttribute)                                             \
  TOK(kDIV, "/", 14, kNoAttribute)                                             \
  TOK(kTRUNCDIV, "~/", 14, kNoAttribute)                                       \
  TOK(kMOD, "%", 14, kNoAttribute)                                             \
                                                                               \
  TOK(kNOT, "!", 0, kNoAttribute)                                              \
  TOK(kCONDITIONAL, "?", 3, kNoAttribute)                                      \
  TOK(kIFnullptr, "??", 4, kNoAttribute)                                       \
                                                                               \
  /* Equality operators.                             */                        \
  /* Please update IsEqualityOperator() if you make  */                        \
  /* any changes to this block.                      */                        \
  TOK(kEQ, "==", 7, kNoAttribute)                                              \
  TOK(kNE, "!=", 7, kNoAttribute)                                              \
  TOK(kEQ_STRICT, "===", 7, kNoAttribute)                                      \
  TOK(kNE_STRICT, "!==", 7, kNoAttribute)                                      \
                                                                               \
  /* Relational operators.                             */                      \
  /* Please update IsRelationalOperator() if you make  */                      \
  /* any changes to this block.                        */                      \
  TOK(kLT, "<", 8, kNoAttribute)                                               \
  TOK(kGT, ">", 8, kNoAttribute)                                               \
  TOK(kLTE, "<=", 8, kNoAttribute)                                             \
  TOK(kGTE, ">=", 8, kNoAttribute)                                             \
                                                                               \
  /* Internal token for !(expr is Type) negative type test operator */         \
  TOK(kISNOT, "", 11, kNoAttribute)                                            \
                                                                               \
  TOK(kINDEX, "[]", 0, kNoAttribute)                                           \
  TOK(kASSIGN_INDEX, "[]=", 0, kNoAttribute)                                   \
  TOK(kNEGATE, "unary-", 0, kNoAttribute)                                      \
                                                                               \
  TOK(kIDENT, "", 0, kNoAttribute)                                             \
  TOK(kSTRING, "", 0, kNoAttribute)                                            \
  TOK(kINTEGER, "", 0, kNoAttribute)                                           \
  TOK(kDOUBLE, "", 0, kNoAttribute)                                            \
                                                                               \
  TOK(kINTERPOL_VAR, "$", 0, kNoAttribute)                                     \
  TOK(kINTERPOL_START, "${", 0, kNoAttribute)                                  \
  TOK(kINTERPOL_END, "}", 0, kNoAttribute)                                     \
                                                                               \
  TOK(kAT, "@", 0, kNoAttribute)                                               \
  TOK(kHASH, "#", 0, kNoAttribute)                                             \
                                                                               \
  TOK(kNEWLINE, "\n", 0, kNoAttribute)                                         \
  TOK(kWHITESP, "", 0, kNoAttribute)                                           \
  TOK(kERROR, "", 0, kNoAttribute)                                             \
  TOK(kILLEGAL, "", 0, kNoAttribute)                                           \
                                                                               \
  /* Support for Dart scripts. */                                              \
  TOK(kSCRIPTTAG, "#!", 0, kNoAttribute)                                       \
                                                                               \
  /* Support for optimized code */                                             \
  TOK(kREM, "rem", 0, kNoAttribute)                                            \
  TOK(kABS, "abs", 0, kNoAttribute)                                            \
  TOK(kSQRT, "sqrt", 0, kNoAttribute)                                          \
  TOK(kMIN, "min", 0, kNoAttribute)                                            \
  TOK(kMAX, "max", 0, kNoAttribute)                                            \
  TOK(kRECIPROCAL, "reciprocal", 0, kNoAttribute)                              \
  TOK(kRECIPROCAL_SQRT, "reciprocal-sqrt", 0, kNoAttribute)                    \
  TOK(kSQUARE, "square", 0, kNoAttribute)                                      \
  TOK(kTRUNCATE, "truncate", 0, kNoAttribute)                                  \
  TOK(kFLOOR, "floor", 0, kNoAttribute)                                        \
  TOK(kCEILING, "ceiling", 0, kNoAttribute)

// List of keywords. The list must be alphabetically ordered. The
// keyword recognition code depends on the ordering.
// If you add a keyword at the beginning or end of this list, make sure
// to update kFirstKeyword and kLastKeyword below.
#define DART_KEYWORD_LIST(KW)                                                  \
  KW(kABSTRACT, "abstract", 0, kPseudoKeyword) /* == kFirstKeyword */          \
  KW(kAS, "as", 11, kPseudoKeyword)                                            \
  KW(kASSERT, "assert", 0, kKeyword)                                           \
  KW(kBREAK, "break", 0, kKeyword)                                             \
  KW(kCASE, "case", 0, kKeyword)                                               \
  KW(kCATCH, "catch", 0, kKeyword)                                             \
  KW(kCLASS, "class", 0, kKeyword)                                             \
  KW(kCONST, "const", 0, kKeyword)                                             \
  KW(kCONTINUE, "continue", 0, kKeyword)                                       \
  KW(kCOVARIANT, "covariant", 0, kPseudoKeyword)                               \
  KW(kDEFAULT, "default", 0, kKeyword)                                         \
  KW(kDEFERRED, "deferred", 0, kPseudoKeyword)                                 \
  KW(kDO, "do", 0, kKeyword)                                                   \
  KW(kELSE, "else", 0, kKeyword)                                               \
  KW(kENUM, "enum", 0, kKeyword)                                               \
  KW(kEXPORT, "export", 0, kPseudoKeyword)                                     \
  KW(kEXTENDS, "extends", 0, kKeyword)                                         \
  KW(kEXTERNAL, "external", 0, kPseudoKeyword)                                 \
  KW(kFACTORY, "factory", 0, kPseudoKeyword)                                   \
  KW(kFALSE, "false", 0, kKeyword)                                             \
  KW(kFINAL, "final", 0, kKeyword)                                             \
  KW(kFINALLY, "finally", 0, kKeyword)                                         \
  KW(kFOR, "for", 0, kKeyword)                                                 \
  KW(kGET, "get", 0, kPseudoKeyword)                                           \
  KW(kIF, "if", 0, kKeyword)                                                   \
  KW(kIMPLEMENTS, "implements", 0, kPseudoKeyword)                             \
  KW(kIMPORT, "import", 0, kPseudoKeyword)                                     \
  KW(kIN, "in", 0, kKeyword)                                                   \
  KW(kIS, "is", 11, kKeyword)                                                  \
  KW(kLIBRARY, "library", 0, kPseudoKeyword)                                   \
  KW(kNEW, "new", 0, kKeyword)                                                 \
  KW(knullptr, "null", 0, kKeyword)                                            \
  KW(kOPERATOR, "operator", 0, kPseudoKeyword)                                 \
  KW(kPART, "part", 0, kPseudoKeyword)                                         \
  KW(kRETHROW, "rethrow", 0, kKeyword)                                         \
  KW(kRETURN, "return", 0, kKeyword)                                           \
  KW(kSET, "set", 0, kPseudoKeyword)                                           \
  KW(kSTATIC, "static", 0, kPseudoKeyword)                                     \
  KW(kSUPER, "super", 0, kKeyword)                                             \
  KW(kSWITCH, "switch", 0, kKeyword)                                           \
  KW(kTHIS, "this", 0, kKeyword)                                               \
  KW(kTHROW, "throw", 0, kKeyword)                                             \
  KW(kTRUE, "true", 0, kKeyword)                                               \
  KW(kTRY, "try", 0, kKeyword)                                                 \
  KW(kTYPEDEF, "typedef", 0, kPseudoKeyword)                                   \
  KW(kVAR, "var", 0, kKeyword)                                                 \
  KW(kVOID, "void", 0, kKeyword)                                               \
  KW(kWHILE, "while", 0, kKeyword)                                             \
  KW(kWITH, "with", 0, kKeyword) /* == kLastKeyword */

class String;

class Token {
 public:
#define T(t, s, p, a) t,
  enum Kind { DART_TOKEN_LIST(T) DART_KEYWORD_LIST(T) kNumTokens };
#undef T

  enum Attribute {
    kNoAttribute = 0,
    kKeyword = 1 << 0,
    kPseudoKeyword = 1 << 1,
  };

  static const Kind kFirstKeyword = kABSTRACT;
  static const Kind kLastKeyword = kWITH;
  static constexpr int kNumKeywords = kLastKeyword - kFirstKeyword + 1;

  static bool IsCommutativeOp(Kind op) {
    switch (op) {
      case Token::kMUL:
        FALL_THROUGH;
      case Token::kADD:
        FALL_THROUGH;
      case Token::kBIT_AND:
        FALL_THROUGH;
      case Token::kBIT_OR:
        FALL_THROUGH;
      case Token::kBIT_XOR:
        return true;
      default:
        return false;
    }
  }

  static bool IsAssignmentOperator(Kind tok) {
    return kASSIGN <= tok && tok <= kASSIGN_COND;
  }

  static bool IsRelationalOperator(Kind tok) {
    return kLT <= tok && tok <= kGTE;
  }

  static bool IsEqualityOperator(Kind tok) {
    return kEQ <= tok && tok <= kNE_STRICT;
  }

  static bool IsStrictEqualityOperator(Kind tok) {
    return (tok == kEQ_STRICT) || (tok == kNE_STRICT);
  }

  static bool IsTypeTestOperator(Kind tok) {
    return (tok == kIS) || (tok == kISNOT);
  }

  static bool IsTypeCastOperator(Kind tok) { return tok == kAS; }

  static bool IsIndexOperator(Kind tok) {
    return tok == kINDEX || tok == kASSIGN_INDEX;
  }

  static bool IsPseudoKeyword(Kind tok) {
    return (Attributes(tok) & kPseudoKeyword) != 0;
  }

  static bool IsKeyword(Kind tok) { return (Attributes(tok) & kKeyword) != 0; }

  static bool IsIdentifier(Kind tok) {
    return (tok == kIDENT) || IsPseudoKeyword(tok);
  }

  static const char* Name(Kind tok) {
    ASSERT(tok < kNumTokens);
    return name_[tok];
  }

  static const char* Str(Kind tok) {
    ASSERT(tok < kNumTokens);
    return tok_str_[tok];
  }

  static bool FromStr(const char* str, Kind* out) {
    ASSERT(str != nullptr && out != nullptr);
#define TOK_CASE(t, s, p, a)                                                   \
  if (strcmp(str, tok_str_[(t)]) == 0) {                                       \
    *out = (t);                                                                \
    return true;                                                               \
  }
    DART_TOKEN_LIST(TOK_CASE)
    DART_KEYWORD_LIST(TOK_CASE)
#undef TOK_CASE
    return false;
  }

  static Attribute Attributes(Kind tok) {
    ASSERT(tok < kNumTokens);
    return attributes_[tok];
  }

  static bool CanBeOverloaded(Kind tok) {
    ASSERT(tok < kNumTokens);
    return IsRelationalOperator(tok) || (tok == kEQ) ||
           (tok >= kADD && tok <= kMOD) ||      // Arithmetic operations.
           (tok >= kBIT_OR && tok <= kUSHR) ||  // Bit operations.
           (tok == kINDEX) || (tok == kASSIGN_INDEX);
  }

  static bool NeedsLiteralToken(Kind tok) {
    ASSERT(tok < kNumTokens);
    return ((tok == Token::kINTEGER) || (tok == Token::kSTRING) ||
            (tok == Token::kINTERPOL_VAR) || (tok == Token::kERROR) ||
            (tok == Token::kDOUBLE));
  }

  static bool IsBinaryOperator(Token::Kind token);
  static bool IsUnaryOperator(Token::Kind token);

  static bool IsBinaryArithmeticOperator(Token::Kind token);
  static bool IsUnaryArithmeticOperator(Token::Kind token);

  static bool IsBinaryBitwiseOperator(Token::Kind token);

  // For a comparison operation return an operation for the negated comparison:
  // !(a (op) b) === a (op') b
  static Token::Kind NegateComparison(Token::Kind op) {
    switch (op) {
      case Token::kEQ:
        return Token::kNE;
      case Token::kNE:
        return Token::kEQ;
      case Token::kLT:
        return Token::kGTE;
      case Token::kGT:
        return Token::kLTE;
      case Token::kLTE:
        return Token::kGT;
      case Token::kGTE:
        return Token::kLT;
      case Token::kEQ_STRICT:
        return Token::kNE_STRICT;
      case Token::kNE_STRICT:
        return Token::kEQ_STRICT;
      case Token::kIS:
        return Token::kISNOT;
      case Token::kISNOT:
        return Token::kIS;
      default:
        UNREACHABLE();
        return Token::kILLEGAL;
    }
  }

  // For a comparison operation return an operation for the equivalent flipped
  // comparison: a (op) b === b (op') a.
  static Token::Kind FlipComparison(Token::Kind op) {
    switch (op) {
      case Token::kEQ:
        return Token::kEQ;
      case Token::kNE:
        return Token::kNE;
      case Token::kLT:
        return Token::kGT;
      case Token::kGT:
        return Token::kLT;
      case Token::kLTE:
        return Token::kGTE;
      case Token::kGTE:
        return Token::kLTE;
      case Token::kEQ_STRICT:
        return Token::kEQ_STRICT;
      case Token::kNE_STRICT:
        return Token::kNE_STRICT;
      default:
        UNREACHABLE();
        return Token::kILLEGAL;
    }
  }

 private:
  static const char* const name_[];
  static const char* const tok_str_[];
  static const Attribute attributes_[];
};

}  // namespace dart

#endif  // RUNTIME_VM_TOKEN_H_
