【问题标题】:'IDENTIFIER' rule also consumes keyword in ANTLR Lexer grammar'IDENTIFIER' 规则还使用 ANTLR Lexer 语法中的关键字
【发布时间】:2017-07-02 10:44:02
【问题描述】:

在处理用于 Java 解析的 Antlr 3.5 语法时,注意到“IDENTIFIER”规则在 ANTLR Lexer 语法中消耗的关键字很少。 Lexer 语法是

lexer grammar JavaLexer;

options {
   //k=8;
   language=Java;
   filter=true;
   //backtrack=true;
}

@lexer::header {
package java;
}

@lexer::members {
public ArrayList<String> keywordsList = new ArrayList<String>();
}

V_DECLARATION
:
( ((MODIFIERS)=>tok1=MODIFIERS WS+)? tok2=TYPE WS+ var=V_DECLARATOR WS* )
{...};

fragment
V_DECLARATOR
  :
  (
    tok=IDENTIFIER WS* ( ',' | ';' | ASSIGN WS* V_VALUE )
  )
  {...};

fragment
V_VALUE
: (IDENTIFIER (DOT WS* IDENTIFIER WS* '(' | ',' | ';'))
;

MODIFIERS
  :
  (PUBLIC | PRIVATE | FINAL)+
;

PRIVATE
    :    tok = 'private'
    { keywordsList.add($tok.getText());  }
    ;

PUBLIC
    :    tok = 'public'
    { keywordsList.add($tok.getText()); }
    ;

DOT
    :    '.'
    { keywordsList.add("."); }
    ;

THIS
    :    tok = 'this'
    { keywordsList.add($tok.getText()); }
    ;

ASSIGN
    :    '='
        { keywordsList.add("="); }
    ;    

IDENTIFIER:
  tok =Identifier
  {  
   //System.out.println("Identifier: " + $tok.text);
  }
  ;  

fragment
Identifier 
    :   (Letter (Letter|JavaIDDigit)*);

fragment
Letter
    :  '\u0024' |
       '\u0041'..'\u005a' |
       '\u005f' |
       '\u0061'..'\u007a' |
       '\u00c0'..'\u00d6' |
       '\u00d8'..'\u00f6' |
       '\u00f8'..'\u00ff' |
       '\u0100'..'\u1fff' |
       '\u3040'..'\u318f' |
       '\u3300'..'\u337f' |
       '\u3400'..'\u3d2d' |
       '\u4e00'..'\u9fff' |
       '\uf900'..'\ufaff'
    ;

fragment
JavaIDDigit
    :  '\u0030'..'\u0039' |
       '\u0660'..'\u0669' |
       '\u06f0'..'\u06f9' |
       '\u0966'..'\u096f' |
       '\u09e6'..'\u09ef' |
       '\u0a66'..'\u0a6f' |
       '\u0ae6'..'\u0aef' |
       '\u0b66'..'\u0b6f' |
       '\u0be7'..'\u0bef' |
       '\u0c66'..'\u0c6f' |
       '\u0ce6'..'\u0cef' |
       '\u0d66'..'\u0d6f' |
       '\u0e50'..'\u0e59' |
       '\u0ed0'..'\u0ed9' |
       '\u1040'..'\u1049'
   ;

WS  :  (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN; skip();}
    ;

当我尝试解析该行时:

public final int inch = this.getValue();

那么规则 'VAR_VALUE -> IDENTIFIER' 也会消耗 "this" 关键字,这是不可取的,因为关键字也会被收集到一个单独的列表中。

Antlr 语法中是否有任何技巧/规定可以通过自身规则匹配关键字而不影响“IDENTIFIER”等其他功能?

【问题讨论】:

  • 也尝试了选项{backtrack=true;},但没有结果。
  • 我觉得规则 V_DECLARATION、V_DECLARATOR 和 V_VALUE 属于解析器,而不是词法分析器。
  • 没有@Maurice Perry,这些是非常基本的词法分析器规则,用于匹配特定的标记,然后在各种规则中触发包含完整Java语法的解析器。
  • 哦!还有修饰符
  • 顺便说一句,修饰符之间不应该有WS吗?

标签: java parsing antlr tokenize antlr3


【解决方案1】:

你的问题确实是对什么属于lexer和什么属于parser的误解造成的:

  • Lexer 的工作是确定字符流代表哪些单词
    • 例如thisTHIS0NUMBERthatIDENTIFIER
  • 解析器的工作是判断词法分析器发出的单词序列是否符合给定的语言,即由这些单词组成的“句子”是否有意义
    • 例如该声明由可能的修饰符、类型和标识符列表组成

由于词法分析器的工作是确定输入中的单词,它处理输入并查找最长有效匹配(在 ANTLR 中,如果两个或多个规则接受相同的输入,则在源语法获胜)。不是针对任何“最具体”的,而是针对最长的。

例子:

  • 输入t
    • 可以是THISIDENTIFIER
  • 输入h
    • 仍然可以是THISIDENTIFIER
  • 输入a
    • 不能再是THIS,只能是IDENTIFIER
  • 输入t
    • IDENTIFIER 肯定的
  • 输入.
    • 不再匹配IDENTIFIER,因此that将匹配为IDENTIFIER,最后输入.将匹配为下一个令牌的新开始

还有一个例子:

  • 输入this
    • 可以一直匹配为THISIDENTIFIER
  • 输入.
    • 不能再被任何东西匹配,所以this 将被匹配为THIS(最上面的匹配规则)而不是IDENTIFIER. 将开始一个新的令牌

现在是重要的部分 - 只要一个词法分析器规则是从另一个词法分析器规则引用的,它就被认为只是引用词法分析器规则的一个片段。这意味着匹配它不会发出新的令牌,并且它也不会在片段匹配结束时触发多个匹配令牌之间的任何决策。由于this 确实可以被IDENTIFIER 规则匹配,整个声明符合V_DECLARATION 词法分析器规则——所以除非有另一个词法分析器 规则可以匹配至少相同长度的输入并且在语法中比这条规则更早,这条规则将适用。

您没有提供任何引用 THIS 的规则,因此我们不知道这在您的语法中究竟如何发挥作用,但显而易见的原因是词法分析器可以匹配更长的输入或更早的规则,而不是使用 @987654358 的任何规则@规则。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多