如何从解析树到Java类文件

发布于 2025-01-18 02:29:18 字数 940 浏览 5 评论 0原文

我正在使用具有以下功能的命令行工具:

  1. 使用扩展的Antlr4 Java9语法修改了.java文件。文件中的语法是Java,对包括目的的方法进行了一个修改,例如在此示例中:public void {Marketing} sendemail(){} {}
  2. 使用访客收集并删除所有目的。目的的收集和分析是程序的主要功能。
  3. 编译并执行删除目的的Java文件。

我正在寻找实现步骤3的最简单,最有效的方法。建立完整的编译器的项目范围不超出我的项目范围,我希望在可能的情况下利用Java编译器并运行Javac。我已经考虑了以下方法,但是没有看似最佳的:

任何输入都非常感谢。

I am working on a command-line tool with the following functionality:

  1. Parse modified .java files using an extended ANTLR4 Java9 grammar. The syntax in the files is Java, with one modification to the method declaration which includes a purpose, like in this example: public void {marketing} sendEmail() {}
  2. Collect and remove all purposes using a visitor. Collection and analysis of the purposes is the main functionality of the program.
  3. Compile and execute the Java files where the purposes are removed.

I am searching for the simplest and most effective way to achieve step 3. It is out of the scope of my project to build a full compiler, I would prefer to exploit the Java compiler and run javac if possible. I have considered the following approaches, but none seem optimal:

Any input is much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

睡美人的小仙女 2025-01-25 02:29:18

您可以使用 TokenStreamRewriter 来获取没有目的节点的源代码(或完成许多其他重写任务)。下面是一个来自应用程序的示例,我有条件地将顶级 LIMIT 子句添加到 MySQL 查询:

/**
001     * Parses the query to see if there's already a top-level limit clause. If none was found, the query is
002     * rewritten to include a limit clause with the given values.
003     *
004     * @param query The query to check and modify.
005     * @param serverVersion The version of MySQL to use for checking.
006     * @param sqlMode The current SQL mode in the server.
007     * @param offset The limit offset to add.
008     * @param count The row count value to add.
009     *
010     * @returns The rewritten query if the original query is error free and contained no top-level LIMIT clause.
011     *          Otherwise the original query is returned.
012     */
013    public checkAndApplyLimits(query: string, serverVersion: number, sqlMode: string, offset: number,
014        count: number): [string, boolean] {
015
016        this.applyServerDetails(serverVersion, sqlMode);
017        const tree = this.startParsing(query, false, MySQLParseUnit.Generic);
018        if (!tree || this.errors.length > 0) {
019            return [query, false];
020        }
021
022        const rewriter = new TokenStreamRewriter(this.tokenStream);
023        const expressions = XPath.findAll(tree, "/query/simpleStatement//queryExpression", this.parser);
024        let changed = false;
025        if (expressions.size > 0) {
026            // There can only be one top-level query expression where we can add a LIMIT clause.
027            const candidate: ParseTree = expressions.values().next().value;
028
029            // Check if the candidate comes from a subquery.
030            let run: ParseTree | undefined = candidate;
031            let invalid = false;
032            while (run) {
033                if (run instanceof SubqueryContext) {
034                    invalid = true;
035                    break;
036                }
037
038                run = run.parent;
039            }
040
041            if (!invalid) {
042                // Top level query expression here. Check if there's already a LIMIT clause before adding one.
043                const context = candidate as QueryExpressionContext;
044                if (!context.limitClause() && context.stop) {
045                    // OK, ready to add an own limit clause.
046                    rewriter.insertAfter(context.stop, ` LIMIT ${offset}, ${count}`);
047                    changed = true;
048                }
049            }
040        }
051
052        return [rewriter.getText(), changed];
053    }

这段代码在做什么:

  • 第 017 行:解析输入以获取解析树。如果你已经这样做了,当然可以传入解析树,而不用再次解析。
  • 第 022 行使用您的令牌流准备一个新的 TokenStreamRewriter 实例。
  • 第023行使用ANTLR4的XPATH功能来获取特定上下文类型的所有节点。您可以在这里一次性检索所有目的上下文。这也是你的第2)点的解决方案。
  • 以下行仅检查是否必须添加新的 LIMIT 子句。对你来说没那么有趣。
  • 第 046 行是操作令牌流的地方。在这种情况下,会添加一些内容,但您也可以 替换或删除节点。
  • 第 052 行可能包含您最感兴趣的内容:它返回输入的原始文本,但应用了所有重写操作。

使用此代码,您可以创建一个临时 java 文件进行编译。它可用于同时执行列表中的两个操作(收集目的并删除它们)。

You could use TokenStreamRewriter to get the source code without the purpose node (or accomplish many other rewriting tasks). Here's an example from an application where I conditionally add a top level LIMIT clause to a MySQL query:

/**
001     * Parses the query to see if there's already a top-level limit clause. If none was found, the query is
002     * rewritten to include a limit clause with the given values.
003     *
004     * @param query The query to check and modify.
005     * @param serverVersion The version of MySQL to use for checking.
006     * @param sqlMode The current SQL mode in the server.
007     * @param offset The limit offset to add.
008     * @param count The row count value to add.
009     *
010     * @returns The rewritten query if the original query is error free and contained no top-level LIMIT clause.
011     *          Otherwise the original query is returned.
012     */
013    public checkAndApplyLimits(query: string, serverVersion: number, sqlMode: string, offset: number,
014        count: number): [string, boolean] {
015
016        this.applyServerDetails(serverVersion, sqlMode);
017        const tree = this.startParsing(query, false, MySQLParseUnit.Generic);
018        if (!tree || this.errors.length > 0) {
019            return [query, false];
020        }
021
022        const rewriter = new TokenStreamRewriter(this.tokenStream);
023        const expressions = XPath.findAll(tree, "/query/simpleStatement//queryExpression", this.parser);
024        let changed = false;
025        if (expressions.size > 0) {
026            // There can only be one top-level query expression where we can add a LIMIT clause.
027            const candidate: ParseTree = expressions.values().next().value;
028
029            // Check if the candidate comes from a subquery.
030            let run: ParseTree | undefined = candidate;
031            let invalid = false;
032            while (run) {
033                if (run instanceof SubqueryContext) {
034                    invalid = true;
035                    break;
036                }
037
038                run = run.parent;
039            }
040
041            if (!invalid) {
042                // Top level query expression here. Check if there's already a LIMIT clause before adding one.
043                const context = candidate as QueryExpressionContext;
044                if (!context.limitClause() && context.stop) {
045                    // OK, ready to add an own limit clause.
046                    rewriter.insertAfter(context.stop, ` LIMIT ${offset}, ${count}`);
047                    changed = true;
048                }
049            }
040        }
051
052        return [rewriter.getText(), changed];
053    }

What is this code doing:

  • Line 017: the input is parsed to get a parse tree. If you have done that already, you can pass in the parse tree, of course, instead of parsing again.
  • Line 022 prepares a new TokenStreamRewriter instance with your token stream.
  • Line 023 uses ANTLR4's XPATH feature to get all nodes of a specific context type. This is where you can retrieve all your purpose contexts in one go. This would also be a solution for your point 2).
  • The following lines only check if a new LIMIT clause must be added at all. Not so interesting for you.
  • Line 046 is the place where you manipulate the token stream. In this case something is added, but you can also replace or remove nodes.
  • Line 052 contains probably what you are most interested in: it returns the original text of the input, but with all the rewrite actions applied.

With this code you can create a temporary java file for compilation. And it could be used to execute two actions from your list at the same time (collect the purposes and remove them).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文