如何在 antlr4 语法中实现 typedef hack

问题描述

我并不完全需要 typedef。我需要别名(对于 shell 语言)。但是查找标识符并返回不同的标记类型的技巧是我需要使语法正常工作。我不一定需要在词法分析器中完成它,尽管这对我来说看起来最干净(或者在词法分析器和解析器之间的阶段)。

鉴于我对 antlr4 的了解,这是我似乎最接近解决方案的(片段),但它需要每个关键字标记的整个级别的非终结符。请注意,根据 Antlr4 大写单词或标记,小写单词是非终结符。

  aliasstmt: alias ident ident; // rule that makes aliases
  ifstmt: if expression then statement;  // sample rule with two keywords

  // non-terminals converting aliases into keywords
  alias: Alias // normal token for keyword
       // hack,LookupAlias is map,I need. 
       | { LookupAlias(_input.LT(1).getText()).equals("alias") }? Ident 
       ;
  if   : If
       | { LookupAlias(_input.LT(1).getText()).equals("if") }? Ident 
       ;
  then : Then
       | { LookupAlias(_input.LT(1).getText()).equals("then") }? Ident 
       ;

  // Non-terminal going the other way,converting keywords to identifiers when needed
  ident : Ident
        | Alias
        | If
        | Then
        ;

现在,我想,我可以摆脱关键字的令牌,并在此示例的解析器中完成所有操作。它不会完全适用于我正在解析的语言,因为大量关键字具有“正常”拼写,例如“Set-Alias”或“-Name”,它们不是合法标识符(以及“Set-Alias”或“ Set -Alias”与“Set-Alias”不同,呃)。

但是,我希望 LookupAlias() 函数成为它自己的 Java 类,而不仅仅是嵌入在解析器中的东西。我有其他时候需要我们它不是解析的一部分,而那些时候需要协调。如何做到这一点是我会问的一个单独的问题。

解决方法

(注意...也许别名可以在我不知道的地方在shell中使用,所以这是基于我的理解)

在 shell 中,别名本质上是一个标识符,当遇到它时会被扩展。仅预期 command 可能出现的位置,并且由于您无法知道路径中的所有命令,因此您的语法可能会在解析器的该位置具有 IDENTIFIER 标记(或类似标记)规则。

然后,您将根据内置命令列表、PATH 中的命令和别名(我不确定优先级,TBH)来检查它。

因此,您需要保留一个符号表来查找别名解析。我认为后分辨率是事情会变得“棘手”的地方。 IIRC,别名不必在语法上完整,你不能真正期望预先解析它们(它们可能不会正确解析)。此外,它们几乎被“注入”到输入流中。通过这种方式,它们更像是预处理器宏。我看不到检测它们的太多方法,构建扩展的输入流并对其进行词法分析/解析。

我想您可以编写一个自定义 TokenStream,它检测别名并响应 getNextToken()(以及在特定索引处获取令牌的方法等)。这将允许在令牌流中的任何地方使用别名,这可能会变得很奇怪,而且提供有用的错误消息可能是魔鬼。 (我猜你只需要将它们指向别名本身)。这种方法将在解析器要求下一个标记时提供别名定义标记来代替别名。我看不出有什么方法可以使用动作/谓词来改变 ANTLR 对它刚刚看到的标记的看法:)

我怀疑使用现有的 shell,在命令行中创建无效的别名替换,并观察错误消息,可能会深入了解其他 shell 如何处理它。我的印象是,shell 预处理输入并替换别名和 ENV 变量等,然后重新解析结果以供执行。

我很确定尝试修改 tokenStream,因为解析器已经在处理它,要么不可行,要么走上疯狂之路。