Tolerant PHP Parser(容错的PHP解析器)
这是一个早期设计的 PHP 解析器,从一开始就是为 IDE 使用场景设计的(有关详细信息,请参阅设计目标)。还有大量的工作要做,所以在这一点上,这个仓库主要是作为一个实验和对话的开始。
开始
在配置您的计算机之后,您可以使用解析器通过友好的 API 生成和使用抽象语法树(AST)。
<?php //自动加载所需的类 require __DIR__ . "/vendor/autoload.php"; use Microsoft\PhpParser\{DiagnosticsProvider, Node, Parser, PositionUtilities}; // Instantiate new parser instance $parser = new Parser(); // Return and print an AST from string contents $astNode = $parser->parseSourceFile('<?php /* comment */ echo "hi!"'); var_dump($astNode); // Gets and prints errors from AST Node. The parser handles errors gracefully, // so it can be used in IDE usage scenarios (where code is often incomplete). $errors = DiagnosticsProvider::getDiagnostics($astNode); var_dump($errors); // Traverse all Node descendants of $astNode foreach ($astNode->getDescendantNodes() as $descendant) { if ($descendant instanceof Node\StringLiteral) { // Print the Node text (without whitespace or comments) var_dump($descendant->getText()); // All Nodes link back to their parents, so it's easy to navigate the tree. $grandParent = $descendant->getParent()->getParent(); var_dump($grandParent->getNodeKindName()); // The AST is fully-representative, and round-trippable to the original source. // This enables consumers to build reliable formatting and refactoring tools. var_dump($grandParent->getLeadingCommentAndWhitespaceText()); } // In addition to retrieving all children or descendants of a Node, // Nodes expose properties specific to the Node type. if ($descendant instanceof Node\Expression\EchoExpression) { $echoKeywordStartPosition = $descendant->echoKeyword->getStartPosition(); // To cut down on memory consumption, positions are represented as a single integer // index into the document, but their line and character positions are easily retrieved. $lineCharacterPosition = PositionUtilities::getLineCharacterPositionFromPosition( $echoKeywordStartPosition, $descendant->getFileContents() ); echo "line: $lineCharacterPosition->line, character: $lineCharacterPosition->character"; } }
注意: API 尚未最终定稿,所以请通过文件问题让我们知道您想公开哪些功能,然后我们再看看我们能做什么!另外,请在解析树中记录任何具有意外行为的错误。我们还在早期阶段,非常感谢您的任何反馈。
设计目标
- 容错设计 -- 在 IDE 场景中,根据定义,代码是不完整的。在输入无效代码的情况下,解析器仍应能够恢复并生成有效的+完整树,以及相关的诊断信息。
- 快速且轻量级(应该能够每秒解析几 MB 的源代码,
为其他功能留出空间)。
- 内存高效的数据结构
- 允许将来进行增量式解析
- 遵守 PHP语言规范, 支持 PHP5 和 PHP7 语法
- 生成的 AST 提供了语义和转换操作所必需的属性(具有完全代表性等),这些属性也需要有效。
- 完全具有代表性,并可回溯到被解析的文本(解析树中包括所有空格和注释细节)。
- 可以通过父/子节点轻松遍历树
- &lt; 100 ms UI响应时间, 所以每个语言服务器操作应该是&lt; 50毫秒,为所有其他并行进行的事情留出空间。
- 随着时间的推移,简单且可维护的 -- 解析器往往会变得非常混乱,非常快,所以可读性和可调试性是高优先级的。
- 可测试 -- 解析器应该生成可证明有效的解析树。我们通过定义和持续测试一组关于树的不变量来实现这一点。
- 友好和描述性的 API,来使其他人在其上能够轻松构建。
- 用PHP编写 -- 尽可能简化 PHP 社区的使用和贡献。
当前状态和方法
为了确保在每一步的正确性足够的水平, 解析器正在使用以下增量方法开发:
- [x] 阶段1:编写不支持PHP语法的词法分析器,但支持EOF 和未知的令牌。为所有不变式编写测试。
- [x] 阶段2:支持PHP词法语法,大量测试
- [x] 阶段3:编写不支持PHP语法的解析器,但生成树 错误节点。为所有不变式编写测试。
- [x] 阶段4:支持PHP语法语法,大量测试
- [] 阶段5(正在进行中:正在运行:):真实世界验证和优化
-
正确性: 验证示例代码库中没有产生错误,与其他解析器进行基准测试(调查任何不一致的实例),fuzz-testing >
,针对大型PHP应用程序进行基准测试
- [] 阶段6:完成API以尽可能简化人们的使用。
其他备注
一些PHP语法结构(即 yield-expression 和模板字符串)还不受支持,还有其他各种各样的bug。但是,由于解析器具有容错能力,所以可以优雅地处理这些错误,否则生成的树是完整的。要更全面地了解我们所处的位置,您可以运行验证测试套件(请参阅贡献指南以获得关于运行测试的更多信息)。或者简单地看一下当前验证测试结果。
尽管我们尚未开始性能优化阶段,但到目前为止,我们已经看到了令人鼓舞的结果,并且还有很多改进的余地。 有关我们当前方法的详细信息,请参见工作原理,并在您自己的计算机上运行性能测试以亲自体验。
这个项目采用了 Microsoft开源行为准则。 有关更多信息,请参阅 行为准则常见问题解答或联系 opencode@microsoft.com 以及其他任何问题或评论。
(The first version translated by vz on 2020.07.26)