anchor.diff:      :  [a-zA-Z]+  // No tld has numbers in it
       ;
   
  -EMAIL       :        (EMailWord EMailDOT?)+ EMailAT EMailWord ( EMailDOT EMailWord )* EMailDOT EMailTLD;
  -
  -CURLYBRACEOPEN :     '{'                 ;
  -CURLYBRACECLOSE:     '}'                 ;
  -BRACEOPEN   :        '('                 ;
  -BRACECLOSE  :        ')'                 ;
  -BLOCKOPEN   :        '['                 ;
  -BLOCKCLOSE  :        ']'                 ;
  -SEMICOLON   :        ';'                 ;
  -COLON       :        ':'                 ;
  -COMMA       :        ','                 ;
  -SLASH       :        '/'                 ;
  -EQUALS      :        '='                 ;
  -MINUS       :        '-'                 ;
  -PLUS        :        '+'                 ;
  +fragment EMailWords
  +    : EMailWord ( EMailDOT EMailWord )*
  +    ;
  +
  +EMAIL
  +    : EMailWords EMailAT EMailWords EMailDOT EMailTLD
  +    ;
  +
  +CURLYBRACEOPEN  : '{' ;
  +CURLYBRACECLOSE : '}' ;
  +BRACEOPEN       : '(' ;
  +BRACECLOSE      : ')' ;
  +BLOCKOPEN       : '[' ;
  +BLOCKCLOSE      : ']' ;
  +SEMICOLON       : ';' ;
  +COLON           : ':' ;
  +COMMA           : ',' ;
  +SLASH           : '/' ;
  +EQUALS          : '=' ;
  +MINUS           : '-' ;
  +PLUS            : '+' ;
   
   // HexWord is 4 hex digits long
   fragment HexDigit: [a-fA-F0-9];
   fragment HexWord : HexDigit HexDigit HexDigit HexDigit ;
  -UUID        :        // 550e8400-e29b-41d4-a716-446655440000
  -                     HexWord HexWord '-' HexWord '-' HexWord '-' HexWord '-' HexWord HexWord HexWord;
  +UUID  // 550e8400-e29b-41d4-a716-446655440000
  +    : HexWord HexWord '-' HexWord '-' HexWord '-' HexWord '-' HexWord HexWord HexWord
  +    ;
   
   fragment BareHostname:  [a-zA-Z0-9\-_]+ ('.'[a-zA-Z0-9\-_]+)*;
   fragment UrlPath     :  [a-zA-Z0-9\-_~=?&%+.:/#]*;
   fragment BasicURL    :  ('http'|'ftp') 's'? '://' BareHostname UrlPath ;
   fragment HTMLURL     :  '<a href="' BasicURL '">'~[<]+'</a>';
  -URL         :        ( '<'? ('www.'BareHostname UrlPath|BasicURL) '>'? | HTMLURL | 'index.htm' UrlPath);
  -
  -GIBBERISH   : '@'(~[ ;])*;
  +URL
  +    : ( '<'? ('www.'BareHostname UrlPath|BasicURL) '>'? | HTMLURL | 'index.htm' UrlPath)
  +    ;
  +
  +GIBBERISH
  +    : '@'(~[ ;])*
  +    ;
   
   // A version is a WORD with at least 1 number in it (and that can contain a '-').
   VERSION
  
positive.code:     ;
  
  // We want to avoid matching against normal names and uuids so a BASE64 needs to be pretty long
  BASE64: B64FirstChunk B64Chunk B64Chunk B64Chunk B64Chunk B64Chunk B64Chunk B64Chunk+ B64LastChunk;
  
  // =========================================================================================
  // Parser
  
  userAgent
      : (SEMICOLON|COMMA|MINUS|'\''|'"'|'\\'|';'|'='|BRACEOPEN|BLOCKOPEN)*                // Leading garbage
        ( (SEMICOLON|COMMA|MINUS)? ( product | rootElements ) )*
        (SEMICOLON|COMMA|MINUS|PLUS|'\''|'"'|'\\'|';'|'='|BRACECLOSE|BLOCKCLOSE)*       // Trailing garbage
      ;
  
  rootElements
  
negative.code: 
  rootText
      : VERSION
      | WORD+
      | GIBBERISH
      | MINUS
      ;
  