content: // Copyright (c) 2011, 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.
  
  part of scanner;
  
  const int EOF_TOKEN = 0;
  
  const int KEYWORD_TOKEN = $k;
  const int IDENTIFIER_TOKEN = $a;
  const int BAD_INPUT_TOKEN = $X;
  const int DOUBLE_TOKEN = $d;
  const int INT_TOKEN = $i;
  const int HEXADECIMAL_TOKEN = $x;
  const int STRING_TOKEN = $SQ;
  
  const int AMPERSAND_TOKEN = $AMPERSAND;
  const int BACKPING_TOKEN = $BACKPING;
  const int BACKSLASH_TOKEN = $BACKSLASH;
  const int BANG_TOKEN = $BANG;
  const int BAR_TOKEN = $BAR;
  const int COLON_TOKEN = $COLON;
  const int COMMA_TOKEN = $COMMA;
  const int EQ_TOKEN = $EQ;
  const int GT_TOKEN = $GT;
  const int HASH_TOKEN = $HASH;
  const int OPEN_CURLY_BRACKET_TOKEN = $OPEN_CURLY_BRACKET;
  const int OPEN_SQUARE_BRACKET_TOKEN = $OPEN_SQUARE_BRACKET;
  const int OPEN_PAREN_TOKEN = $OPEN_PAREN;
  const int LT_TOKEN = $LT;
  const int MINUS_TOKEN = $MINUS;
  const int PERIOD_TOKEN = $PERIOD;
  const int PLUS_TOKEN = $PLUS;
  const int QUESTION_TOKEN = $QUESTION;
  const int AT_TOKEN = $AT;
  const int CLOSE_CURLY_BRACKET_TOKEN = $CLOSE_CURLY_BRACKET;
  const int CLOSE_SQUARE_BRACKET_TOKEN = $CLOSE_SQUARE_BRACKET;
  const int CLOSE_PAREN_TOKEN = $CLOSE_PAREN;
  const int SEMICOLON_TOKEN = $SEMICOLON;
  const int SLASH_TOKEN = $SLASH;
  const int TILDE_TOKEN = $TILDE;
  const int STAR_TOKEN = $STAR;
  const int PERCENT_TOKEN = $PERCENT;
  const int CARET_TOKEN = $CARET;
  
  const int STRING_INTERPOLATION_TOKEN = 128;
  const int LT_EQ_TOKEN = STRING_INTERPOLATION_TOKEN + 1;
  const int FUNCTION_TOKEN = LT_EQ_TOKEN + 1;
  const int SLASH_EQ_TOKEN = FUNCTION_TOKEN + 1;
  const int PERIOD_PERIOD_PERIOD_TOKEN = SLASH_EQ_TOKEN + 1;
  const int PERIOD_PERIOD_TOKEN = PERIOD_PERIOD_PERIOD_TOKEN + 1;
  const int EQ_EQ_EQ_TOKEN = PERIOD_PERIOD_TOKEN + 1;
  const int EQ_EQ_TOKEN = EQ_EQ_EQ_TOKEN + 1;
  const int LT_LT_EQ_TOKEN = EQ_EQ_TOKEN + 1;
  const int LT_LT_TOKEN = LT_LT_EQ_TOKEN + 1;
  const int GT_EQ_TOKEN = LT_LT_TOKEN + 1;
  const int GT_GT_EQ_TOKEN = GT_EQ_TOKEN + 1;
  const int INDEX_EQ_TOKEN = GT_GT_EQ_TOKEN + 1;
  const int INDEX_TOKEN = INDEX_EQ_TOKEN + 1;
  const int BANG_EQ_EQ_TOKEN = INDEX_TOKEN + 1;
  const int BANG_EQ_TOKEN = BANG_EQ_EQ_TOKEN + 1;
  const int AMPERSAND_AMPERSAND_TOKEN = BANG_EQ_TOKEN + 1;
  const int AMPERSAND_EQ_TOKEN = AMPERSAND_AMPERSAND_TOKEN + 1;
  const int BAR_BAR_TOKEN = AMPERSAND_EQ_TOKEN + 1;
  const int BAR_EQ_TOKEN = BAR_BAR_TOKEN + 1;
  const int STAR_EQ_TOKEN = BAR_EQ_TOKEN + 1;
  const int PLUS_PLUS_TOKEN = STAR_EQ_TOKEN + 1;
  const int PLUS_EQ_TOKEN = PLUS_PLUS_TOKEN + 1;
  const int MINUS_MINUS_TOKEN = PLUS_EQ_TOKEN + 1;
  const int MINUS_EQ_TOKEN = MINUS_MINUS_TOKEN + 1;
  const int TILDE_SLASH_EQ_TOKEN = MINUS_EQ_TOKEN + 1;
  const int TILDE_SLASH_TOKEN = TILDE_SLASH_EQ_TOKEN + 1;
  const int PERCENT_EQ_TOKEN = TILDE_SLASH_TOKEN + 1;
  const int GT_GT_TOKEN = PERCENT_EQ_TOKEN + 1;
  const int CARET_EQ_TOKEN = GT_GT_TOKEN + 1;
  const int COMMENT_TOKEN = CARET_EQ_TOKEN + 1;
  const int STRING_INTERPOLATION_IDENTIFIER_TOKEN = COMMENT_TOKEN + 1;
  
  /**
   * A token that doubles as a linked list.
   */
  abstract class Token implements Spannable {
    /**
     * The character offset of the start of this token within the source text.
     */
    final int charOffset;
  
    Token(this.charOffset);
  
    /**
     * The next token in the token stream.
     */
    Token next;
  
    /**
     * The precedence info for this token. [info] determines the kind and the
     * precedence level of this token.
     *
     * Defined as getter to save a field in the [KeywordToken] subclass.
     */
    PrecedenceInfo get info;
  
    /**
     * The string represented by this token, a substring of the source code.
     *
     * For [StringToken]s the [value] includes the quotes, explicit escapes, etc.
     */
    String get value;
  
    /**
     * For symbol and keyword tokens, returns the string value represented by this
     * token. For [StringToken]s this method returns [:null:].
     *
     * For [SymbolToken]s and [KeywordToken]s, the string value is a compile-time
     * constant originating in the [PrecedenceInfo] or in the [Keyword] instance.
     * This allows testing for keywords and symbols using [:identical:], e.g.,
     * [:identical('class', token.value):].
     *
     * Note that returning [:null:] for string tokens is important to identify
     * symbols and keywords, we cannot use [value] instead. The string literal
     *   "$a($b"
     * produces ..., SymbolToken($), StringToken(a), StringToken((), ...
     *
     * After parsing the identifier 'a', the parser tests for a function
     * declaration using [:identical(next.stringValue, '('):], which (rihgtfully)
     * returns false because stringValue returns [:null:].
     */
    String get stringValue;
  
    /**
     * The kind enum of this token as determined by its [info].
     */
    int get kind => info.kind;
  
    /**
     * The precedence level for this token.
     */
    int get precedence => info.precedence;
  
    /**
     * True if this token is an identifier. Some keywords allowed as identifiers,
     * see implementation in [KeywordToken].
     */
    bool isIdentifier();
  
    /**
     * Returns a textual representation of this token to be used for debugging
     * purposes. The resulting string might contain information about the
     * structure of the token, for example 'StringToken(foo)' for the identifier
     * token 'foo'.
     *
     * Use [value] for the text actually parsed by the token.
     */
    String toString();
  
    /**
     * The number of characters parsed by this token.
     */
    int get charCount {
      if (info == BAD_INPUT_INFO) {
        // This is a token that wraps around an error message. Return 1
        // instead of the size of the length of the error message.
        return 1;
      } else {
        return value.length;
      }
    }
  
    int get hashCode => computeHashCode(charOffset, info, value);
  }
  
  /**
   * A [SymbolToken] represents the symbol in its precendence info.
   * Also used for end of file with EOF_INFO.
   */
  class SymbolToken extends Token {
  
    final PrecedenceInfo info;
  
    SymbolToken(this.info, int charOffset) : super(charOffset);
  
    String get value => info.value;
  
    String get stringValue => info.value;
  
    bool isIdentifier() => false;
  
    String toString() => "SymbolToken($value)";
  }
  
  /**
   * A [BeginGroupToken] represents a symbol that may be the beginning of
   * a pair of brackets, i.e., ( { [ < or ${
   * The [endGroup] token points to the matching closing bracked in case
   * it can be identified during scanning.
   */
  class BeginGroupToken extends SymbolToken {
    Token endGroup;
  
    BeginGroupToken(PrecedenceInfo info, int charOffset)
        : super(info, charOffset);
  }
  
  /**
   * A keyword token.
   */
  class KeywordToken extends Token {
    final Keyword keyword;
  
    KeywordToken(this.keyword, int charOffset) : super(charOffset);
  
    PrecedenceInfo get info => keyword.info;
  
    String get value => keyword.syntax;
  
    String get stringValue => keyword.syntax;
  
    bool isIdentifier() => keyword.isPseudo || keyword.isBuiltIn;
  
    String toString() => "KeywordToken($value)";
  }
  
  /**
   * A String-valued token. Represents identifiers, string literals,
   * number literals, comments, and error tokens, using the corresponding
   * precedence info.
   */
  class StringToken extends Token {
    /**
     * The length threshold above which substring tokens are computed lazily.
     *
     * For string tokens that are substrings of the program source, the actual
     * substring extraction is performed lazily. This is beneficial because
     * not all scanned code is actually used. For unused parts, the substrings
     * are never computed and allocated.
     */
    static const int LAZY_THRESHOLD = 4;
  
    var /* String | LazySubtring */ valueOrLazySubstring;
  
    final PrecedenceInfo info;
  
    /**
     * Creates a non-lazy string token. If [canonicalize] is true, the string
     * is canonicalized before the token is created.
     */
    StringToken.fromString(this.info, String value, int charOffset,
                           {bool canonicalize : false})
        : valueOrLazySubstring = canonicalizedString(value, canonicalize),
          super(charOffset);
  
    /**
     * Creates a lazy string token. If [canonicalize] is true, the string
     * is canonicalized before the token is created.
     */
    StringToken.fromSubstring(this.info, String data, int start, int end,
                              int charOffset, {bool canonicalize : false})
        : super(charOffset) {
      int length = end - start;
      if (length <= LAZY_THRESHOLD) {
        valueOrLazySubstring = canonicalizedString(data.substring(start, end),
                                                   canonicalize);
      } else {
        valueOrLazySubstring =
            new LazySubstring(data, start, length, canonicalize);
      }
    }
  
    /**
     * Creates a lazy string token. If [asciiOnly] is false, the byte array
     * is passed through a UTF-8 decoder.
     */
    StringToken.fromUtf8Bytes(this.info, List<int> data, int start, int end,
                              bool asciiOnly, int charOffset)
        : super(charOffset) {
      int length = end - start;
      if (length <= LAZY_THRESHOLD) {
        valueOrLazySubstring = decodeUtf8(data, start, end, asciiOnly);
      } else {
        valueOrLazySubstring = new LazySubstring(data, start, length, asciiOnly);
      }
    }
  
    String get value {
      if (valueOrLazySubstring is String) {
        return valueOrLazySubstring;
      } else {
        assert(valueOrLazySubstring is LazySubstring);
        var data = valueOrLazySubstring.data;
        int start = valueOrLazySubstring.start;
        int end = start + valueOrLazySubstring.length;
        if (data is String) {
          valueOrLazySubstring = canonicalizedString(
              data.substring(start, end), valueOrLazySubstring.boolValue);
        } else {
          valueOrLazySubstring = decodeUtf8(
              data, start, end, valueOrLazySubstring.boolValue);
        }
        return valueOrLazySubstring;
      }
    }
  
    String get stringValue => null;
  
    bool isIdentifier() => identical(kind, IDENTIFIER_TOKEN);
  
    String toString() => "StringToken($value)";
  
    static final HashSet<String> canonicalizedSubstrings =
        new HashSet<String>();
  
    static String canonicalizedString(String s, bool canonicalize) {
      if (!canonicalize) return s;
      var result = canonicalizedSubstrings.lookup(s);
      if (result != null) return result;
      canonicalizedSubstrings.add(s);
      return s;
    }
  
    static String decodeUtf8(List<int> data, int start, int end, bool asciiOnly) {
      var s;
      if (asciiOnly) {
        // getRange returns an iterator, it does not copy the data.
        s = new String.fromCharCodes(data.getRange(start, end));
      } else {
        // TODO(lry): this is measurably slow. Also sublist is copied eagerly.
        var bytes = data.sublist(start, end);
        s = UTF8.decode(bytes);
      }
      return canonicalizedString(s, true);
    }
  }
  
  /**
   * This class represents the necessary information to compute a substring
   * lazily. The substring can either originate from a string or from
   * a [:List<int>:] of UTF-8 bytes.
   */
  abstract class LazySubstring {
    /** The original data, either a string or a List<int> */
    get data;
  
    int get start;
    int get length;
  
    /**
     * If this substring is based on a String, the [boolValue] indicates wheter
     * the resulting substring should be canonicalized.
     *
     * For substrings based on a byte array, the [boolValue] is true if the
     * array only holds ASCII characters. The resulting substring will be
     * canonicalized after decoding.
     */
    bool get boolValue;
  
    LazySubstring.internal();
  
    factory LazySubstring(data, int start, int length, bool b) {
      // See comment on [CompactLazySubstring].
      if (start < 0x100000 && length < 0x200) {
        int fields = (start << 9);
        fields = fields | length;
        fields = fields << 1;
        if (b) fields |= 1;
        return new CompactLazySubstring(data, fields);
      } else {
        return new FullLazySubstring(data, start, length, b);
      }
    }
  }
  
  /**
   * This class encodes [start], [length] and [boolValue] in a single
   * 30 bit integer. It uses 20 bits for [start], which covers source files
   * of 1MB. [length] has 9 bits, which covers 512 characters.
   *
   * The file html_dart2js.dart is currently around 1MB.
   */
  class CompactLazySubstring extends LazySubstring {
    final data;
    final int fields;
  
    CompactLazySubstring(this.data, this.fields) : super.internal();
  
    int get start => fields >> 10;
    int get length => (fields >> 1) & 0x1ff;
    bool get boolValue => (fields & 1) == 1;
  }
  
  class FullLazySubstring extends LazySubstring {
    final data;
    final int start;
    final int length;
    final bool boolValue;
    FullLazySubstring(this.data, this.start, this.length, this.boolValue)
        : super.internal();
  }
  
  bool isUserDefinableOperator(String value) {
    return
        isBinaryOperator(value) ||
        isMinusOperator(value) ||
        isTernaryOperator(value) ||
        isUnaryOperator(value);
  }
  
  bool isUnaryOperator(String value) => identical(value, '~');
  
  bool isBinaryOperator(String value) {
    return
        (identical(value, '==')) ||
        (identical(value, '[]')) ||
        (identical(value, '*')) ||
        (identical(value, '/')) ||
        (identical(value, '%')) ||
        (identical(value, '~/')) ||
        (identical(value, '+')) ||
        (identical(value, '<<')) ||
        (identical(value, '>>')) ||
        (identical(value, '>=')) ||
        (identical(value, '>')) ||
        (identical(value, '<=')) ||
        (identical(value, '<')) ||
        (identical(value, '&')) ||
        (identical(value, '^')) ||
        (identical(value, '|'));
  }
  
  bool isTernaryOperator(String value) => identical(value, '[]=');
  
  bool isMinusOperator(String value) => identical(value, '-');
  
  class PrecedenceInfo {
    final String value;
    final int precedence;
    final int kind;
  
    const PrecedenceInfo(this.value, this.precedence, this.kind);
  
    toString() => 'PrecedenceInfo($value, $precedence, $kind)';
  
    int get hashCode => computeHashCode(value, precedence, kind);
  }
  
  // TODO(ahe): The following are not tokens in Dart.
  const PrecedenceInfo BACKPING_INFO =
    const PrecedenceInfo('`', 0, BACKPING_TOKEN);
  const PrecedenceInfo BACKSLASH_INFO =
    const PrecedenceInfo('\\', 0, BACKSLASH_TOKEN);
  const PrecedenceInfo PERIOD_PERIOD_PERIOD_INFO =
    const PrecedenceInfo('...', 0,
                         PERIOD_PERIOD_PERIOD_TOKEN);
  
  /**
   * The cascade operator has the lowest precedence of any operator
   * except assignment.
   */
  const int CASCADE_PRECEDENCE = 2;
  const PrecedenceInfo PERIOD_PERIOD_INFO =
    const PrecedenceInfo('..', CASCADE_PRECEDENCE,
                         PERIOD_PERIOD_TOKEN);
  
  const PrecedenceInfo BANG_INFO =
    const PrecedenceInfo('!', 0, BANG_TOKEN);
  const PrecedenceInfo COLON_INFO =
    const PrecedenceInfo(':', 0, COLON_TOKEN);
  const PrecedenceInfo INDEX_INFO =
    const PrecedenceInfo('[]', 0, INDEX_TOKEN);
  const PrecedenceInfo MINUS_MINUS_INFO =
    const PrecedenceInfo('--', POSTFIX_PRECEDENCE,
                         MINUS_MINUS_TOKEN);
  const PrecedenceInfo PLUS_PLUS_INFO =
    const PrecedenceInfo('++', POSTFIX_PRECEDENCE,
                         PLUS_PLUS_TOKEN);
  const PrecedenceInfo TILDE_INFO =
    const PrecedenceInfo('~', 0, TILDE_TOKEN);
  
  const PrecedenceInfo FUNCTION_INFO =
    const PrecedenceInfo('=>', 0, FUNCTION_TOKEN);
  const PrecedenceInfo HASH_INFO =
    const PrecedenceInfo('#', 0, HASH_TOKEN);
  const PrecedenceInfo INDEX_EQ_INFO =
    const PrecedenceInfo('[]=', 0, INDEX_EQ_TOKEN);
  const PrecedenceInfo SEMICOLON_INFO =
    const PrecedenceInfo(';', 0, SEMICOLON_TOKEN);
  const PrecedenceInfo COMMA_INFO =
    const PrecedenceInfo(',', 0, COMMA_TOKEN);
  
  const PrecedenceInfo AT_INFO =
    const PrecedenceInfo('@', 0, AT_TOKEN);
  
  // Assignment operators.
  const int ASSIGNMENT_PRECEDENCE = 1;
  const PrecedenceInfo AMPERSAND_EQ_INFO =
    const PrecedenceInfo('&=',
                         ASSIGNMENT_PRECEDENCE, AMPERSAND_EQ_TOKEN);
  const PrecedenceInfo BAR_EQ_INFO =
    const PrecedenceInfo('|=',
                         ASSIGNMENT_PRECEDENCE, BAR_EQ_TOKEN);
  const PrecedenceInfo CARET_EQ_INFO =
    const PrecedenceInfo('^=',
                         ASSIGNMENT_PRECEDENCE, CARET_EQ_TOKEN);
  const PrecedenceInfo EQ_INFO =
    const PrecedenceInfo('=',
                         ASSIGNMENT_PRECEDENCE, EQ_TOKEN);
  const PrecedenceInfo GT_GT_EQ_INFO =
    const PrecedenceInfo('>>=',
                         ASSIGNMENT_PRECEDENCE, GT_GT_EQ_TOKEN);
  const PrecedenceInfo LT_LT_EQ_INFO =
    const PrecedenceInfo('<<=',
                         ASSIGNMENT_PRECEDENCE, LT_LT_EQ_TOKEN);
  const PrecedenceInfo MINUS_EQ_INFO =
    const PrecedenceInfo('-=',
                         ASSIGNMENT_PRECEDENCE, MINUS_EQ_TOKEN);
  const PrecedenceInfo PERCENT_EQ_INFO =
    const PrecedenceInfo('%=',
                         ASSIGNMENT_PRECEDENCE, PERCENT_EQ_TOKEN);
  const PrecedenceInfo PLUS_EQ_INFO =
    const PrecedenceInfo('+=',
                         ASSIGNMENT_PRECEDENCE, PLUS_EQ_TOKEN);
  const PrecedenceInfo SLASH_EQ_INFO =
    const PrecedenceInfo('/=',
                         ASSIGNMENT_PRECEDENCE, SLASH_EQ_TOKEN);
  const PrecedenceInfo STAR_EQ_INFO =
    const PrecedenceInfo('*=',
                         ASSIGNMENT_PRECEDENCE, STAR_EQ_TOKEN);
  const PrecedenceInfo TILDE_SLASH_EQ_INFO =
    const PrecedenceInfo('~/=',
                         ASSIGNMENT_PRECEDENCE, TILDE_SLASH_EQ_TOKEN);
  
  const PrecedenceInfo QUESTION_INFO =
    const PrecedenceInfo('?', 3, QUESTION_TOKEN);
  
  const PrecedenceInfo BAR_BAR_INFO =
    const PrecedenceInfo('||', 4, BAR_BAR_TOKEN);
  
  const PrecedenceInfo AMPERSAND_AMPERSAND_INFO =
    const PrecedenceInfo('&&', 5, AMPERSAND_AMPERSAND_TOKEN);
  
  const PrecedenceInfo BAR_INFO =
    const PrecedenceInfo('|', 8, BAR_TOKEN);
  
  const PrecedenceInfo CARET_INFO =
    const PrecedenceInfo('^', 9, CARET_TOKEN);
  
  const PrecedenceInfo AMPERSAND_INFO =
    const PrecedenceInfo('&', 10, AMPERSAND_TOKEN);
  
  // Equality operators.
  const int EQUALITY_PRECEDENCE = 6;
  const PrecedenceInfo BANG_EQ_EQ_INFO =
    const PrecedenceInfo('!==',
                         EQUALITY_PRECEDENCE, BANG_EQ_EQ_TOKEN);
  const PrecedenceInfo BANG_EQ_INFO =
    const PrecedenceInfo('!=',
                         EQUALITY_PRECEDENCE, BANG_EQ_TOKEN);
  const PrecedenceInfo EQ_EQ_EQ_INFO =
    const PrecedenceInfo('===',
                         EQUALITY_PRECEDENCE, EQ_EQ_EQ_TOKEN);
  const PrecedenceInfo EQ_EQ_INFO =
    const PrecedenceInfo('==',
                         EQUALITY_PRECEDENCE, EQ_EQ_TOKEN);
  
  // Relational operators.
  const int RELATIONAL_PRECEDENCE = 7;
  const PrecedenceInfo GT_EQ_INFO =
    const PrecedenceInfo('>=',
                         RELATIONAL_PRECEDENCE, GT_EQ_TOKEN);
  const PrecedenceInfo GT_INFO =
    const PrecedenceInfo('>',
                         RELATIONAL_PRECEDENCE, GT_TOKEN);
  const PrecedenceInfo IS_INFO =
    const PrecedenceInfo('is',
                         RELATIONAL_PRECEDENCE, KEYWORD_TOKEN);
  const PrecedenceInfo AS_INFO =
    const PrecedenceInfo('as',
                         RELATIONAL_PRECEDENCE, KEYWORD_TOKEN);
  const PrecedenceInfo LT_EQ_INFO =
    const PrecedenceInfo('<=',
                         RELATIONAL_PRECEDENCE, LT_EQ_TOKEN);
  const PrecedenceInfo LT_INFO =
    const PrecedenceInfo('<',
                         RELATIONAL_PRECEDENCE, LT_TOKEN);
  
  // Shift operators.
  const PrecedenceInfo GT_GT_INFO =
    const PrecedenceInfo('>>', 11, GT_GT_TOKEN);
  const PrecedenceInfo LT_LT_INFO =
    const PrecedenceInfo('<<', 11, LT_LT_TOKEN);
  
  // Additive operators.
  const PrecedenceInfo MINUS_INFO =
    const PrecedenceInfo('-', 12, MINUS_TOKEN);
  const PrecedenceInfo PLUS_INFO =
    const PrecedenceInfo('+', 12, PLUS_TOKEN);
  
  // Multiplicative operators.
  const PrecedenceInfo PERCENT_INFO =
    const PrecedenceInfo('%', 13, PERCENT_TOKEN);
  const PrecedenceInfo SLASH_INFO =
    const PrecedenceInfo('/', 13, SLASH_TOKEN);
  const PrecedenceInfo STAR_INFO =
    const PrecedenceInfo('*', 13, STAR_TOKEN);
  const PrecedenceInfo TILDE_SLASH_INFO =
    const PrecedenceInfo('~/', 13, TILDE_SLASH_TOKEN);
  
  const int POSTFIX_PRECEDENCE = 14;
  const PrecedenceInfo PERIOD_INFO =
    const PrecedenceInfo('.', POSTFIX_PRECEDENCE,
                         PERIOD_TOKEN);
  
  const PrecedenceInfo KEYWORD_INFO =
    const PrecedenceInfo('keyword', 0, KEYWORD_TOKEN);
  
  const PrecedenceInfo EOF_INFO =
    const PrecedenceInfo('EOF', 0, EOF_TOKEN);
  
  const PrecedenceInfo IDENTIFIER_INFO =
    const PrecedenceInfo('identifier', 0, IDENTIFIER_TOKEN);
  
  const PrecedenceInfo BAD_INPUT_INFO =
    const PrecedenceInfo('malformed input', 0,
                         BAD_INPUT_TOKEN);
  
  const PrecedenceInfo OPEN_PAREN_INFO =
    const PrecedenceInfo('(', POSTFIX_PRECEDENCE,
                         OPEN_PAREN_TOKEN);
  
  const PrecedenceInfo CLOSE_PAREN_INFO =
    const PrecedenceInfo(')', 0, CLOSE_PAREN_TOKEN);
  
  const PrecedenceInfo OPEN_CURLY_BRACKET_INFO =
    const PrecedenceInfo('{', 0, OPEN_CURLY_BRACKET_TOKEN);
  
  const PrecedenceInfo CLOSE_CURLY_BRACKET_INFO =
    const PrecedenceInfo('}', 0, CLOSE_CURLY_BRACKET_TOKEN);
  
  const PrecedenceInfo INT_INFO =
    const PrecedenceInfo('int', 0, INT_TOKEN);
  
  const PrecedenceInfo STRING_INFO =
    const PrecedenceInfo('string', 0, STRING_TOKEN);
  
  const PrecedenceInfo OPEN_SQUARE_BRACKET_INFO =
    const PrecedenceInfo('[', POSTFIX_PRECEDENCE,
                         OPEN_SQUARE_BRACKET_TOKEN);
  
  const PrecedenceInfo CLOSE_SQUARE_BRACKET_INFO =
    const PrecedenceInfo(']', 0, CLOSE_SQUARE_BRACKET_TOKEN);
  
  const PrecedenceInfo DOUBLE_INFO =
    const PrecedenceInfo('double', 0, DOUBLE_TOKEN);
  
  const PrecedenceInfo STRING_INTERPOLATION_INFO =
    const PrecedenceInfo('\${', 0,
                         STRING_INTERPOLATION_TOKEN);
  
  const PrecedenceInfo STRING_INTERPOLATION_IDENTIFIER_INFO =
    const PrecedenceInfo('\$', 0,
                         STRING_INTERPOLATION_IDENTIFIER_TOKEN);
  
  const PrecedenceInfo HEXADECIMAL_INFO =
    const PrecedenceInfo('hexadecimal', 0, HEXADECIMAL_TOKEN);
  
  const PrecedenceInfo COMMENT_INFO =
    const PrecedenceInfo('comment', 0, COMMENT_TOKEN);
  