【问题标题】:ParserRule matching the wrong tokenParserRule 匹配错误的标记
【发布时间】:2016-08-13 09:37:16
【问题描述】:

我正在尝试学习一点 ANTLR4 并为某些 4GL 语言定义语法。

这就是我所拥有的:

compileUnit
:
    typedeclaration EOF
;

typedeclaration
:
    ID LPAREN DATATYPE INT RPAREN
;

DATATYPE
:
    DATATYPE_ALPHANUMERIC
    | DATATYPE_NUMERIC
;

DATATYPE_ALPHANUMERIC
:
    'A'
;

DATATYPE_NUMERIC
:
    'N'
;

fragment
DIGIT
:
    [0-9]
;

fragment
LETTER
:
    [a-zA-Z]
;

INT
:
    DIGIT+
;

ID
:
    LETTER
    (
        LETTER
        | DIGIT
    )*
;

LPAREN
:
    '('
;

RPAREN
:
    ')'
;

WS
:
    [ \t\f]+ -> skip
;

我希望能够解析的内容:

测试(A10)

我得到了什么:

typedeclaration:1:6: 不匹配的输入 'A10' 需要 DATATYPE

但是我可以写:

测试(A 10)

为什么我需要在这里放一个空格? LPAREN DATATYPE 本身是有效的,所以中间不需要空格。 INT RPAREN 也在工作。 为什么 DATATYPE 和 INT 之间需要一个空格?我对此有点困惑。 我猜它是匹配 ID,因为它是“最长”匹配,但这里必须有一些方法可以强制变得更懒,对吧?

【问题讨论】:

  • 因为A10 是 ID 的有效令牌。你想匹配第一个变体还是两者都匹配? ANTLR 总是尽可能长时间地匹配标记(并且在解析之前创建标记)。
  • @CoronA 我想匹配第一个变体,中间没有空格。
  • 我可以想到以下解决方案:显式包含空格(而不是跳过),使 ID 不以 AN 开头,或者接受括号内的任何 ID 并过滤掉语义上的错误分析。有偏好吗?

标签: antlr antlr4


【解决方案1】:

您应该忽略 ID 第一位的“A”和“N”聊天。正如@CoronA 注意到的那样,ANTLR 尽可能长地匹配令牌(ID 'A10' 的长度大于 DATATYPE_ALPHANUMERIC 'A' 的长度)。另请阅读:Priority rules。尝试使用以下语法:

grammar expr;

compileUnit
    : typedeclaration EOF
    ;

typedeclaration
    : ID LPAREN datatype INT RPAREN
    ;

datatype
    : DATATYPE_ALPHANUMERIC
    | DATATYPE_NUMERIC
    ;

DATATYPE_ALPHANUMERIC
    : 'A'
    ;

DATATYPE_NUMERIC
    : 'N'
    ;

INT
    : DIGIT+
    ;

ID
    : [b-mo-zB-MO-Z] (LETTER | DIGIT)*
;

LPAREN
    : '('
    ;

RPAREN
    : ')'
    ;

WS
    : [ \t\f]+ -> skip
    ;

fragment
DIGIT
    : [0-9]
    ;

fragment
LETTER
    : [a-zA-Z]
    ;

您也可以使用以下语法而不受 id 限制。数据类型将比字母更早被识别。也不清楚:

grammar expr;

compileUnit
    : typedeclaration EOF
    ;

typedeclaration
    : id LPAREN datatype DIGIT+ RPAREN
    ;

id
    : (datatype | LETTER) (datatype | LETTER | DIGIT)*
    ;

datatype
    : DATATYPE_ALPHANUMERIC
    | DATATYPE_NUMERIC
    ;

DATATYPE_ALPHANUMERIC: 'A';
DATATYPE_NUMERIC:      'N';
// List with another Data types.
LETTER:                [a-zA-Z];

LPAREN
    : '('
    ;

RPAREN
    : ')'
    ;

WS
    : [ \t\f]+ -> skip
    ;

DIGIT
    : [0-9]
    ;

【讨论】:

  • 感谢您的示例@KvanTTT,我有点明白这是怎么回事。但是现在我的标识符不能以 A 或 N 开头,这不是我们想要的行为。有 11 种数据类型只用一个字母表示,为了便于阅读,我省略了除 2 种以外的所有数据类型
  • 我可以让 typedeclaration 期望一个 ID 而不是数据类型,并在侦听器/访问者中提取它,但这不会是“语法气味”吗? :-)
  • @MarkusA。我删除了身份限制。如果此语法不适合您,则应使用侦听器/访问者方法。
  • 我替换了您答案中的 SO 文档链接,因为文档正在关闭。它指向相同的内容,但我已经扩展了一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 1970-01-01
相关资源
最近更新 更多