phpactor / tolerant-php-parser
为 IDE 使用场景设计的容错 PHP 到 AST 解析器
Requires
- php: >=7.2
Requires (Dev)
- phpstan/phpstan: ^1.8
- phpunit/phpunit: ^8.5.15
This package is auto-updated.
Last update: 2024-09-03 17:22:23 UTC
README
这是一个早期阶段的 PHP 解析器,从一开始就设计用于 IDE 使用场景(更多详情请参阅 设计目标)。还有很多工作要做,所以目前,这个仓库主要作为一个实验和对话的开始。
这是 v0.1 分支,该分支更改了数据结构以支持在初始 0.0.x 发布线之后添加的语法。
开始使用
配置好您的机器后,您可以使用解析器通过友好的 API 生成和操作抽象语法树 (AST)。
<?php // Autoload required classes 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 提供了语义和转换操作所需的属性(完全代表性等),同时也需要高效。
- 完全代表并且能够从解析出的文本中回绕(包括所有空白和注释 "trivia")
- 能够通过父子节点轻松遍历树
- < 100 毫秒 UI 响应时间,因此每个语言服务器操作应该 < 50 毫秒,为并行进行的所有其他操作留出空间。
- 简单且易于维护 - 解析器有变得非常混乱的倾向,所以可读性和可调试性是首要任务。
- 可测试 - 解析器应产生可证明有效的解析树。我们通过定义和持续测试关于树的一系列不变量来实现这一点。
- 友好且描述性的 API,使其他人易于构建。
- 用 PHP 编写 - 使 PHP 社区能够尽可能容易地消费和贡献。
当前状态和方法
为确保每一步都达到足够的正确性,解析器正在采用以下增量方法开发
- 第一阶段:编写不支持 PHP 语法的词法分析器,但支持 EOF 和未知标记。为所有不变量编写测试。
- 第二阶段:支持 PHP 词法语法,大量测试
- 第三阶段:编写一个不支持PHP语法的解析器,但能够生成错误节点树。为所有不变性编写测试。
- 第四阶段:支持PHP句法语法,大量测试
- 第五阶段(进行中 🏃):现实世界的验证和优化
- 正确性:验证在样本代码库中不会产生错误,与其他解析器进行基准测试(调查任何不一致的实例),模糊测试
- 性能:分析,与大型PHP应用程序进行基准测试
- 第六阶段:最终确定API,使其尽可能易于人们使用。
附加说明
一些PHP语法结构(特别是yield表达式和模板字符串)尚未支持,也存在其他杂项错误。然而,由于解析器具有容错性,这些错误被优雅地处理,生成的树在其他方面是完整的。要获得一个更全面的了解,您可以运行“验证”测试套件(有关运行测试的更多信息,请参阅贡献指南)。或者,您可以简单地查看当前的验证测试结果。
尽管我们尚未开始性能优化阶段,但我们已经看到了一些有希望的结果,并且还有很大的改进空间。有关我们当前方法的详细信息,请参阅如何工作,并在您的机器上运行性能测试以亲自查看。
了解更多
🎯 设计目标 - 了解项目的目标(功能、性能指标等)。
📖 文档 - 了解如何从您的项目中引用解析器,以及如何对AST执行操作以回答有关您代码的问题。
👀 语法可视化工具 - 获得对AST更直观的感受。发挥创造力 - 看看您能否打破它!
📈 当前状态和做法 - 支持了多少语法?性能?内存?API稳定性?
🔧 如何工作 - 了解架构、设计决策和权衡。
💖 贡献! - 了解如何参与其中,查看一些有助于您熟悉代码库的教育性提交的指针(即使您以前从未编写过解析器),以及推荐的工作流程,这些工作流程使迭代更容易。
本项目已采用微软开源行为准则。有关更多信息,请参阅行为准则常见问题解答或联系opencode@microsoft.com以了解任何其他问题或评论。