【问题标题】:How to get a syntax tree with comments?如何获得带有注释的语法树?
【发布时间】:2018-02-18 17:55:11
【问题描述】:

我正在尝试为多种语言创建文档生成器。为此,我需要一个 AST,以便知道,例如,这个注释是针对一个类的,而这个注释是针对这个类的一个方法的。

我开始编写这个简单的 Python 代码,它通过递归查看树来显示树:

import sys
import antlr4
from ECMAScriptLexer import ECMAScriptLexer
from ECMAScriptParser import ECMAScriptParser

def handleTree(tree, lvl=0):
    for child in tree.getChildren():
        if isinstance(child, antlr4.tree.Tree.TerminalNode):
            print(lvl*'│ ' + '└─', child)
        else:
            handleTree(child, lvl+1)

input = antlr4.FileStream(sys.argv[1])
lexer = ECMAScriptLexer(input)
stream = antlr4.CommonTokenStream(lexer)
parser = ECMAScriptParser(stream)
tree = parser.program()
handleTree(tree)

并尝试用antlr EcmaScript grammar解析这段Javascript代码:

var i = 52; // inline comment

function foo() {
  /** The foo documentation */
  console.log('hey');
}

这个输出:

│ │ │ │ └─ var
│ │ │ │ │ │ └─ i
│ │ │ │ │ │ │ └─ =
│ │ │ │ │ │ │ │ │ │ └─ 52
│ │ │ │ │ └─ ;
│ │ │ └─ function
│ │ │ └─ foo
│ │ │ └─ (
│ │ │ └─ )
│ │ │ └─ {
│ │ │ │ │ │ │ │ │ │ │ │ └─ console
│ │ │ │ │ │ │ │ │ │ │ └─ .
│ │ │ │ │ │ │ │ │ │ │ │ └─ log
│ │ │ │ │ │ │ │ │ │ │ └─ (
│ │ │ │ │ │ │ │ │ │ │ │ │ │ └─ 'hey'
│ │ │ │ │ │ │ │ │ │ │ └─ )
│ │ │ │ │ │ │ │ │ └─ ;
│ │ │ └─ }
└─ <EOF>

所有的cmets都被忽略了,可能是因为channel(HIDDEN)in the grammar的存在。

经过一番谷歌搜索后,我找到了this 这个答案:

除非您有非常令人信服的理由将注释放入解析器(我想听听),否则您应该将其放入词法分析器中。

那么,为什么 cmets 不应该包含在解析器中以及如何获取包含 cmets 的树?

【问题讨论】:

  • Python 将文档与语言元素相关联的方式是通过文档字符串,而不是 cmets。 Docstrings 应该出现在你的 ast 中。使用 cmets,您无法确定某个特定的注释“用于一个类,而这个用于该类的方法”。
  • 抱歉,可能不太清楚:这里我正在尝试使用 Python 编写的解析器解析 JavaScript 代码。

标签: python antlr antlr4


【解决方案1】:

那么,为什么 cmets 不应该包含在解析器中,以及如何获取包含 cmets 的树?

如果您从规则MultiLineComment 中删除-&gt; channel(HIDDEN)

MultiLineComment
 : '/*' .*? '*/' -> channel(HIDDEN)
 ;

那么MultiLineComment 最终会出现在解析器中。但是,您的每个解析器规则都需要在允许的地方包含这些标记。

arrayLiteral解析器规则为例:

/// ArrayLiteral :
///     [ Elision? ]
///     [ ElementList ]
///     [ ElementList , Elision? ]
arrayLiteral
 : '[' elementList? ','? elision? ']'
 ;

因为这是 JavaScript 中有效的数组字面量:

[/* ... */ 1, 2 /* ... */ , 3 /* ... */ /* ... */]

这意味着您需要使用MultiLineComment 标记来处理所有解析器规则,如下所示:

/// ArrayLiteral :
///     [ Elision? ]
///     [ ElementList ]
///     [ ElementList , Elision? ]
arrayLiteral
 : '[' MultiLineComment* elementList? MultiLineComment* ','? MultiLineComment* elision? MultiLineComment* ']'
 ;

这会变成一团糟。

编辑

来自cmets:

所以不可能用 antlr 生成包含 cmets 的树?是否有一些 hack 或其他库可以做到这一点?

还有格罗森伯格的回答:

Antlr 为这个任务提供了一个方便的方法:BufferedTokenStream#getHiddenTokensToLeft。在遍历解析树时,访问流以获取与节点相关的注释(如果有)。使用BufferedTokenStream#getHiddenTokensToRight 获取任何尾随评论。

【讨论】:

  • 感谢您的澄清!所以不可能用antlr生成一棵包含cmets的树?是否有一些 hack 或其他库可以做到这一点?
  • Antlr 为这个任务提供了一个方便的方法:BufferedTokenStream#getHiddenTokensToLeft。在遍历解析树时,访问流以获取与节点相关的注释(如果有)。使用BufferedTokenStream#getHiddenTokensToRight 获取任何尾随评论。
  • 我知道有这样的实用方法,但不知道它们。感谢您提及他们@GRosenberg!添加到答案中。
猜你喜欢
  • 1970-01-01
  • 2013-09-13
  • 1970-01-01
  • 2014-12-09
  • 2018-12-14
  • 2023-04-02
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多