【问题标题】:Is it always possible to go from AST to original source code? [closed]是否总是可以从 AST 转到原始源代码? [关闭]
【发布时间】:2021-10-17 03:15:36
【问题描述】:

JavaScript 源代码可以转换为 AST。我正在使用 SHIFT AST Parser 从 JavaScipt 代码创建 AST。

现在我想将生成的 AST 转换回源代码。

我在这里非常困惑,并试图了解基本原理。我从同事那里听说 AST 无法转换回源代码。但是是什么原因呢?

一位同事告诉我,AST 不保留间距,并且在将 AST 转换为源代码时会丢失缩进。

这是唯一的原因吗?

【问题讨论】:

  • 另一个原因是cmets被丢弃了。
  • 但是丢弃的 cmets 无法恢复。此外,有些人出于某种原因想要保留原始间距和缩进。例如,如果更改间距/缩进,git diff 之类的工具很难有效使用。
  • 嗯......是的......这一切都取决于你在谈论什么 AST 实现。可以设计一个 AST 以绝对保留原始源代码中的所有内容。
  • @Exploring - 不,它们根本不是限制。取决于您所说的“原始来源”是什么意思-如果您想要原始代码中每个错误的缩进、奇数间距、字节对字节,那么不,您可能无法重现-我想我的问题是-为什么要你想要?当然,您可以生成有效的源代码,只是没有原始源代码
  • 当然不是。即使是 CST 传统上也不会保留很多令牌流(空白和 cmets)。 AST 是 CST 的树重写,传统上保留的信息更少。重写功能可能不是一对一的,进一步使源重构成为问题。假设您有一种语言的关键字不区分大小写,因此关键字“func”和“Func”是等价的,但是 AST 不再保留标记,因为 AST 仅表示我们正在定义一个函数。预处理(解析器/重构器的管道)加剧了这个问题。

标签: javascript abstract-syntax-tree lexer shift-ast


【解决方案1】:

首先,这取决于您所说的“原始源”代码。

  • 如果您指的是与您在编写软件时正在编辑的完全相同的文件系统上的完全相同的文件,答案是您不能。很明显。

  • 如果你的意思是一个字符一个字符的代码与你编写的代码相同,这在技术上是可行的,但在实践中不太可能。

  • 如果您的意思是工作方式完全相同且“看起来几乎相同”的代码,那么是的,您可以。 (取决于“大部分”。)

答案还取决于您所谈论的 AST实现

  • 一些 AST 实现不保留 cmets 或间距/缩进。

  • 其他 AST 实现显然可以保留 cmets;例如作为树节点上的装饰。

  • 理论上,AST 实现可以完全保留重建相同源代码所需的一切。 (但我不知道有这样的例子。这会耗费内存并且毫无意义。)


无法恢复cmets有什么危害?

这取决于您要将重新生成的源代码用于什么目的。如果你打算能够替换原来的代码,那么问题就很明显了:

  • 您丢失了程序员包含的任何(希望是)有用的 cmets,以帮助人们理解代码。

  • 通常以程式化的源代码 cmets 的形式嵌入正式的 API 文档,然后提取、格式化等。如果这些 cmets 丢失,则很难使 API 文档保持最新。

  • 一些第 3 方工具将程式化的 cmets 用于特定目的。例如,可以使用注释来抑制静态代码分析器的误报;例如Python 代码中的 # noqa 注释会抑制 pep8 样式错误。

另一方面......这种事情可能与您的用例无关。


现在从标签中我推断出您正在使用Shift-AST。通过对文档和源代码的简要扫描,我认为这不会保留 cmets 或缩进/间距。

这意味着您无法恢复与原始代码逐个字符相同的源代码。如果那是您想要的……您的同事是 100% 正确的。

但是,字符与字符相同的代码可能不是必需的,因此这可能不是限制。这取决于您的用例。

您可以调查Babel 作为替代方案。显然它可以保存cmets。


一位同事告诉我,AST 不保留间距,并且在将 AST 转换为源代码时会丢失缩进。 这是唯一的原因吗?

显然,不。(正如我的回答所解释的那样。)

【讨论】:

  • "我不知道有这样的例子。它会消耗内存并且毫无意义" - linters 和自动格式化程序使用这些。但是,它不再称为 abstract 语法树,而是称为 parse treeconcrete syntax tree
【解决方案2】:

不。 abstract syntax treeabstract,因为它 抽象 去除了模棱两可的语法,例如空格和可能还有 cmets(如果这些与进一步处理)。由于存储这些信息通常没有任何目的,因此值得在解析期间删除。

虽然不能回到“原始源代码”,但仍然可以回到通常称为规范形式的等效表示。

【讨论】:

  • 还有分组运算符、分号、尾随逗号等
【解决方案3】:

嗯,这是可能的。如果你使用 shift-ast,你可以做到。

第 1 步:

npm install shift-codegen

第 2 步:

import codegen from "shift-codegen";
let programSource = codegen(/* Shift format AST */);

ProgramSource 将返回字符串。将其写入您的文件并使用prettier 格式化您的代码。

shift-ast 的替代品称为babel 提供了很多好处、转换和模板功能。还提供 typescript、flow、jsx 和 cmets 以及缩小功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多