/**
   * Extract a Pascal special symbol token from the source.
   *
   * @throws Exception if an error occurred.
   */
  protected void extract() throws Exception {
    char currentChar = currentChar();

    text = Character.toString(currentChar);
    type = null;

    switch (currentChar) {

        // The following symbols are groups that contain multi-character symbols
        // !, !=
        // &, &=, &&
        // ^, ^=
        // *, */, *=
        // -, --, -=
        // +, ++, +=
        // =, ==
        // |, |=, ||
        // /, /=, //, /*
        // <, <<=, <<, <=
        // >, >>=, >=, >>

        // The following symbols are single-character
        // :
        // ;
        // ?
        // @
        // %
        // ~
        // .
        // ,
        // '
        // "
        // (
        // )
        // [
        // ]
        // {
        // }

        // Single-character special symbols.
      case ':':
      case ';':
      case '?':
      case '@':
      case '%':
      case '~':
      case '.':
      case ',':
      case '\'':
      case '"':
      case '(':
      case ')':
      case '[':
      case ']':
      case '{':
      case '}':
        {
          nextChar(); // consume character
          break;
        }

        // ! or !=
      case '!':
        {
          currentChar = nextChar(); // consume ':';

          if (currentChar == '=') {
            text += currentChar;
            nextChar(); // consume '='
          }

          break;
        }

        // & or &= or &&
      case '&':
        {
          currentChar = nextChar(); // consume '<';

          if (currentChar == '=') {
            text += currentChar;
            nextChar(); // consume '='
          } else if (currentChar == '&') {
            text += currentChar;
            nextChar(); // consume '>'
          }

          break;
        }

        // ^ or ^=
      case '^':
        {
          currentChar = nextChar(); // consume '>';

          if (currentChar == '=') {
            text += currentChar;
            nextChar(); // consume '='
          }

          break;
        }

        // * or */ or *=
      case '*':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '/') {
            text += currentChar;
            nextChar(); // consume '.'
          } else if (currentChar == '=') {
            text += currentChar;
            nextChar();
          }

          break;
        }

        // - or -- or -=
      case '-':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '-') {
            text += currentChar;
            nextChar(); // consume '.'
          } else if (currentChar == '=') {
            text += currentChar;
            nextChar();
          }
          break;
        }

        // + or ++ or +=
      case '+':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '+') {
            text += currentChar;
            nextChar(); // consume '.'
          } else if (currentChar == '=') {
            text += currentChar;
            nextChar();
          }
          break;
        }

        // = or ==
      case '=':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '=') {
            text += currentChar;
            nextChar(); // consume '.'
          }
          break;
        }

        // | or |= or ||
      case '|':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '=') {
            text += currentChar;
            nextChar(); // consume '.'
          } else if (currentChar == '|') {
            text += currentChar;
            nextChar();
          }
          break;
        }

        // / or /= or // or /*
      case '/':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '=') {
            text += currentChar;
            nextChar(); // consume '.'
          } else if (currentChar == '/') {
            text += currentChar;
            nextChar();
          } else if (currentChar == '*') {
            text += currentChar;
            nextChar();
          }
          break;
        }

        // < or <<= or << or <=
      case '<':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '<') {
            text += currentChar;
            nextChar(); // consume '.'
            if (currentChar == '=') {
              text += currentChar;
              nextChar();
            }
          } else if (currentChar == '=') {
            text += currentChar;
            nextChar();
          }
          break;
        }

        // > or >>= or >= or >>
      case '>':
        {
          currentChar = nextChar(); // consume '.';

          if (currentChar == '>') {
            text += currentChar;
            nextChar(); // consume '.'
            if (currentChar == '=') {
              text += currentChar;
              nextChar();
            }
          } else if (currentChar == '=') {
            text += currentChar;
            nextChar();
          }
          break;
        }

      default:
        {
          nextChar(); // consume bad character
          type = ERROR;
          value = INVALID_CHARACTER;
        }
    }

    // Set the type if it wasn't an error.
    if (type == null) {
      type = SPECIAL_SYMBOLS.get(text);
    }
  }