@Override
  public StructuredDate evaluate(String displayDate) throws StructuredDateFormatException {
    stack = new Stack<Object>();

    result = new StructuredDate();
    result.setDisplayDate(displayDate);

    // Instantiate a parser from the lowercased display date, so that parsing will be
    // case insensitive.
    ANTLRInputStream inputStream = new ANTLRInputStream(displayDate.toLowerCase());
    StructuredDateLexer lexer = new StructuredDateLexer(inputStream);
    CommonTokenStream tokenStream = new CommonTokenStream(lexer);
    StructuredDateParser parser = new StructuredDateParser(tokenStream);

    // Don't try to recover from parse errors, just bail.
    parser.setErrorHandler(new BailErrorStrategy());

    // Don't print error messages to the console.
    parser.removeErrorListeners();

    // Generate our own custom error messages.
    parser.addParseListener(this);

    try {
      // Attempt to fulfill the oneDisplayDate rule of the grammar.
      parser.oneDisplayDate();
    } catch (ParseCancellationException e) {
      // ParseCancellationException is thrown by the BailErrorStrategy when there is a
      // parse error, with the underlying RecognitionException as the cause.
      RecognitionException re = (RecognitionException) e.getCause();

      throw new StructuredDateFormatException(getErrorMessage(re), re);
    }

    // The parsing was successful. Return the result.
    return result;
  }