/**
   * Extract a Pascal string token from the source.
   *
   * @throws Exception if an error occurred.
   */
  protected void extract() throws Exception {
    StringBuilder textBuffer = new StringBuilder();
    StringBuilder valueBuffer = new StringBuilder();

    char currentChar = nextChar(); // consume initial quote
    textBuffer.append('\"');

    // Get string characters.
    do {
      // Replace any whitespace character with a blank.
      if (Character.isWhitespace(currentChar)) {
        currentChar = ' ';
      }

      if ((currentChar != '"') && (currentChar != EOF) && currentChar != '\\') {
        textBuffer.append(currentChar);
        valueBuffer.append(currentChar);
        currentChar = nextChar(); // consume character
      }
      if (currentChar == '\\') {
        textBuffer.append(currentChar);
        valueBuffer.append(currentChar);
        currentChar = nextChar(); // consume character
        if (currentChar == '"') {
          textBuffer.append(currentChar);
          valueBuffer.append(currentChar);
          currentChar = nextChar(); // consume character
        }
      }

      // Quote?  Each pair of adjacent quotes represents a single-quote.
      if (currentChar == '"') {
        while ((currentChar == '"') && (peekChar() == '"')) {
          textBuffer.append("\"\"");
          valueBuffer.append(currentChar); // append single-quote
          currentChar = nextChar(); // consume pair of quotes
          currentChar = nextChar();
        }
      }
    } while ((currentChar != '"') && (currentChar != EOF));

    if (currentChar == '"') {
      nextChar(); // consume final quote
      textBuffer.append('"');

      type = STRING;
      value = valueBuffer.toString();
    } else {
      type = ERROR;
      value = UNEXPECTED_EOF;
    }

    text = textBuffer.toString();
  }
  /**
   * 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);
    }
  }