【问题标题】:Writing parser rules sensitive to whitespace while skipping WS from the lexer在从词法分析器中跳过 WS 时编写对空格敏感的解析器规则
【发布时间】:2014-07-31 17:45:25
【问题描述】:

我在处理空白时遇到了一些麻烦。在以下语法摘录中,我设置了词法分析器,以便解析器跳过空格:

ENTITY_VAR
    : 'user'
    | 'resource'
    ;

INT : DIGIT+ | '-' DIGIT+ ;
ID : LETTER (LETTER | DIGIT | SPECIAL)* ;
ENTITY_ID : '__' ENTITY_VAR ('_w_' ID)?;

NEWLINE : '\r'? '\n';

WS : [ \t\r\n]+ -> skip; // skip spaces, tabs, newlines

fragment LETTER : [a-zA-Z];
fragment DIGIT : [0-9];
fragment SPECIAL : ('_' | '#' );

问题是,我想匹配ENTITY_ID 形式的变量名称,这样匹配的字符串就没有任何空格。像我在这里所做的那样将其编写为词法分析器规则就足够了,但问题是我想使用解析器规则来代替,因为我想直接访问这两个标记 ENTITY_VAR 和 @ 987654325@ 单独从我的代码中提取,而不是将它们挤回到整个令牌 ENTITY_ID 中。

有什么想法吗? 基本上任何让我直接访问ENTITY_VARID 的解决方案都适合我,无论是将ENTITY_ID 作为词法分析器规则还是将其移至解析器。

【问题讨论】:

  • 也许lexical modes 可以帮忙?一旦你偶然发现'__',你会切换不跳过空格的模式?
  • 感谢您的建议。所以,如果我理解正确,我会编写一个解析器规则entityVar,这样当与'__' 匹配时,它会切换到禁用WS 词法分析器规则的模式?
  • 操作,我的意思是entityId,而不是Var
  • Lexer 模式不依赖于解析器规则。每当词法分析器匹配'__' 时,它都会切换模式,无论是否存在实际使用'__' 标记的解析器规则。
  • 您能否提供一些示例输入代码以及您想要的内容?

标签: antlr grammar antlr4


【解决方案1】:

我能想到几种方法(不按特殊顺序):

  1. 从规则ENTITY_ID 中发出多个标记。请参阅ANTLR4: How to inject tokens 获取灵感
  2. 在解析器中允许空格并在之后检查
  3. 使用单个令牌并在代码中拆分
  4. 使用单个令牌并修改令牌流之前将其传递给解析器。 IE。 lex,修改 ENTITY_ID 标记并将它们拆分为其他几个标记,然后将此流传递给解析器
  5. 不要跳过空格,在处理这些“额外标记”时,请检查它们是否在 ENTITY_ID 部分(=> 是错误)或不在(=> 忽略错误)内。
  6. 不要跳过空格并在语法中允许空格的任何地方添加“WS*”(如果语法不太大,可以)。
  7. 在解析器规则中插入谓词,检查之间是否有空格。
  8. 像这样创建一个“陷阱”规则:

    INVALID_ENTITY_ID : '__' WS+ ENTITY_VAR WS? ('_w_' WS? ID)?
                      | '__' WS? ENTITY_VAR WS+ ('_w_' WS? ID)?
                      | '__' WS? ENTITY_VAR WS? ('_w_' WS+ ID)
                      ;
    

    这将捕获无效的ENTITY_IDs,因为它比那些将成为单个令牌的部分更长。

如果它在“非错误”情况下不会改变解析,我会选择 2,即允许空格不会以不同方式解释代码。

【讨论】:

  • 我最后选择了选项 3。每个其他选项都涉及对语法添加太多复杂的更改,在这种情况下,这不仅仅是值得的。通过从 java 代码中通过正则表达式再次解析它,我能够保持语法简单明了。
【解决方案2】:

据我通过浏览文档了解到,这看起来并不可行。

解析器规则似乎只在默认通道上工作,所以我不能将WS 发送到channel(HIDDEN),然后只为单个解析器规则恢复它。

另一方面,antlr explains here 的作者认为,自第 4 版以来,无法分解任何令牌。

尽管我根本不喜欢它,但似乎最快的方法是从词法分析器中解析它(如问题中的代码),只是为了再次从 Java 中重新解析它字符串。

不过,欢迎任何其他更好的选择或更正我的结论。

【讨论】:

    【解决方案3】:

    按照您自己的回答建议,将两个解析器挂接到某种管道中,是一种合理且简单的设计/解决方案,我很确定 ANTLR 能够提供帮助。

    我不知道 ANTLR 人员在流/提要解析方面的工作进展如何。但是,采用两遍策略应该足够有效,因为第一遍只是对常规语言进行词法分析,即 O(c * N) 超过输入大小,而 c 非常小。

    如果您想要单程花费O(k * N)(k 较大),您可以考虑PEG,其中有implementations in Java(我没有尝试过)。

    【讨论】:

    • 谢谢,但是我要用这个解决方案解析的字符串太小了,以至于使用任何类型的库肯定是矫枉过正。我对如何再次解析 ENTITY_ID 不感兴趣,而是通过使用 Antlr 的单次传递来完全避免它:)
    猜你喜欢
    • 2013-04-29
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多