使用 Antlr 解析语义版本

Parsing Semantic Version using Antlr

我将 SemVer 2 BNF grammar 翻译成以下 Antlr 语法。

grammar SemVer;

@header {
package com.me.semver;
}

semVer : normal ('-' preRelease)? ('+' build)? ;
normal : major '.' minor '.' patch ;
major : NUM ;
minor : NUM ;
patch : NUM ;
preRelease : PRE_RELEASE ('.' preRelease)* ;
build : BUILD ('.' build)*;
NUM : '0'
    | POSITIVE_DIGIT
    | POSITIVE_DIGIT DIGITS
    ;
BUILD : ALPHANUM
      | DIGITS
      ;
PRE_RELEASE : ALPHANUM
            | NUM
            ;
fragment
ALPHANUM : NON_DIGIT
         | NON_DIGIT CHARS
         | CHARS NON_DIGIT
         | CHARS NON_DIGIT CHARS
         ;
fragment
CHARS : CHAR+ ;
fragment
CHAR : DIGIT
     | NON_DIGIT
     ;
fragment
NON_DIGIT : LETTER
          | '-'
          ;
fragment
DIGITS : DIGIT+ ;
fragment
DIGIT : '0'
      | POSITIVE_DIGIT
      ;
fragment
POSITIVE_DIGIT : [1-9] ;
fragment
LETTER : [a-zA-Z] ;

但是解析1.0.0-beta+exp.sha.5114f85出现以下错误:

line 1:4 mismatched input '0-beta' expecting NUM

监听器的输出如下:

Normal: 1.0.0-beta
Major: 1
Minor: 0
Patch: 0-beta
Build: exp.sha.5114f85
Build: sha.5114f85
Build: 5114f85

很明显,补丁版本不是应该的。正确的输出应该是 Patch = 0Pre release = betaBuild = exp.sha.5114f85.

如何修改语法?

您有太多重叠的词法分析器规则。例如,输入 0 可以匹配以下 3 条规则中的任何一条:

NUM : '0'
    | POSITIVE_DIGIT
    | POSITIVE_DIGIT DIGITS
    ;
BUILD : ALPHANUM
      | DIGITS
      ;
PRE_RELEASE : ALPHANUM
            | NUM
            ;

并且由于 NUM 放在第一位,输入 0 将始终成为 NUM 标记。解析器尝试匹配什么标记并不重要,它始终是 NUM 标记。

这就是 ANTLR 的词法分析器的工作原理:

  1. 它会尝试为每个标记匹配尽可能多的字符,并且
  2. 当两个或多个词法分析器规则匹配相同数量的字符时,第一个定义的规则“获胜”。

根据您的语法和输入 "1.0.0-beta+exp.sha.5114f85",将创建这些标记:

NUM                       `1`
null                      `.`
NUM                       `0`
null                      `.`
BUILD                     `0-beta`
null                      `+`
BUILD                     `exp`
null                      `.`
BUILD                     `sha`
null                      `.`
BUILD                     `5114f85`

注意 0-beta 被标记为单个 BUILD 标记(规则 #1)。

您应该做的是定义不重叠的词法分析器规则。在您的情况下,这意味着定义这些 rules/tokens:

HYPHEN
 : '-'
 ;

PLUS
 : '+'
 ;

DOT
 : '.'
 ;

ZERO_DIGIT
 : '0'
 ;

POSITIVE_DIGIT
 : [1-9]
 ;

LETTER
 : [a-zA-Z]
 ;

DIGITDIGITS 这样的规则将成为解析器规则:

digits
 : digit+
 ;

digit
 : ZERO_DIGIT
 | POSITIVE_DIGIT
 ;