anchor.diff:      ;
   
   commentEntry
  -    :  ( emptyWord )
  -    |  ( productNameNoVersion
  -       | keyWithoutValue
  -       | multipleWords
  -       | CURLYBRACEOPEN productNameNoVersion CURLYBRACECLOSE
  -       | CURLYBRACEOPEN keyWithoutValue CURLYBRACECLOSE
  -       )
  -    |  ( commentBlock
  -       | commentProduct
  -       | keyValue
  -       | uuId
  -       | siteUrl
  -       | emailAddress
  -       | versionWord
  -       | base64
  -       | CURLYBRACEOPEN commentProduct  CURLYBRACECLOSE
  -       | CURLYBRACEOPEN keyValue        CURLYBRACECLOSE
  -       | CURLYBRACEOPEN uuId            CURLYBRACECLOSE
  -       | CURLYBRACEOPEN siteUrl         CURLYBRACECLOSE
  -       | CURLYBRACEOPEN emailAddress    CURLYBRACECLOSE
  -       | CURLYBRACEOPEN multipleWords   CURLYBRACECLOSE
  -       | CURLYBRACEOPEN versionWord     CURLYBRACECLOSE
  -       | CURLYBRACEOPEN base64          CURLYBRACECLOSE
  -       )
  -       ( commentBlock
  -       | commentProduct
  -       | keyValue
  -       | uuId
  -       | siteUrl
  -       | emailAddress
  -       | multipleWords
  -       | base64
  -       | CURLYBRACEOPEN commentProduct  CURLYBRACECLOSE
  -       | CURLYBRACEOPEN keyValue        CURLYBRACECLOSE
  -       | CURLYBRACEOPEN uuId            CURLYBRACECLOSE
  -       | CURLYBRACEOPEN siteUrl         CURLYBRACECLOSE
  -       | CURLYBRACEOPEN emailAddress    CURLYBRACECLOSE
  -       | CURLYBRACEOPEN multipleWords   CURLYBRACECLOSE
  -       | CURLYBRACEOPEN versionWord     CURLYBRACECLOSE
  -       | CURLYBRACEOPEN base64          CURLYBRACECLOSE
  -       )*
  -       ( productNameNoVersion
  -       | keyWithoutValue
  -       | CURLYBRACEOPEN productNameNoVersion CURLYBRACECLOSE
  -       | CURLYBRACEOPEN keyWithoutValue CURLYBRACECLOSE
  -       )?
  +    :   ( emptyWord )
  +    |   (
  +            ( commentBlock
  +            | commentProduct
  +            | keyValue
  +            | uuId
  +            | siteUrl
  +            | emailAddress
  +            | versionWord
  +            | base64
  +            | CURLYBRACEOPEN commentProduct  CURLYBRACECLOSE
  +            | CURLYBRACEOPEN keyValue        CURLYBRACECLOSE
  +            | CURLYBRACEOPEN uuId            CURLYBRACECLOSE
  +            | CURLYBRACEOPEN siteUrl         CURLYBRACECLOSE
  +            | CURLYBRACEOPEN emailAddress    CURLYBRACECLOSE
  +            | CURLYBRACEOPEN multipleWords   CURLYBRACECLOSE
  +            | CURLYBRACEOPEN versionWord     CURLYBRACECLOSE
  +            | CURLYBRACEOPEN base64          CURLYBRACECLOSE
  +            )
  +            (MINUS*)
  +        )*
  +        ( multipleWords
  +        | productNameNoVersion
  +        | keyWithoutValue
  +        | CURLYBRACEOPEN productNameNoVersion CURLYBRACECLOSE
  +        | CURLYBRACEOPEN keyWithoutValue CURLYBRACECLOSE
  +        )?
       ;
   
   productNameKeyValue
  
positive.code:     : (SEMICOLON|COMMA|MINUS|'\''|'"'|'\\'|';'|'='|BRACEOPEN|BLOCKOPEN)*                // Leading garbage
        (('user-agent'|'User-Agent'|'UserAgent') (COLON|COMMA|EQUALS|'\\t')*)?            // Leading garbage
        ( (SEMICOLON|COMMA|MINUS)? ( product | emailAddress | siteUrl | rootTextPart SEMICOLON) )*
        ( (SEMICOLON|COMMA|MINUS)? rootTextPart )*                                        // Capture trailing text like "like Gecko"
          (SEMICOLON|COMMA|MINUS|PLUS|'\''|'"'|'\\'|';'|'='|BRACECLOSE|BLOCKCLOSE)*       // Trailing garbage
      ;
  
  
negative.code:     ;
  
  multipleWords
      : (MINUS* WORD)+ MINUS*
      | GIBBERISH
      ;
  
  