首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从解析树获取Java类文件

如何从解析树获取Java类文件
EN

Stack Overflow用户
提问于 2022-03-31 13:54:28
回答 1查看 158关注 0票数 0

我正在开发一个具有以下功能的命令行工具:

  1. 使用扩展的.java Java9语法解析修改后的Java9文件。文件中的语法是Java,对方法声明进行了一次修改,其中包括一个用途,如本例所示:public void {marketing} sendEmail() {}
  2. 使用访客收集并移除所有用途。收集和分析目的是程序的主要功能。
  3. 编译并执行删除目的的Java文件。

我正在寻找实现第3步的最简单、最有效的方法。在我的项目范围之外,构建一个完整的编译器,如果可能的话,我更愿意利用Java编译器并运行javac。我曾考虑过以下方法,但似乎没有一种是最佳的:

任何投入都是非常感谢的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-01 08:05:40

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

代码语言:javascript
复制
/**
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 4的XPATH特性获取特定上下文类型的所有节点。这是您可以一次检索所有目标上下文的地方。这也是您的第2点的解决方案)。
  • 下面的行只检查是否必须添加新的限制子句。对你来说没那么有趣。
  • 第046行是您操作令牌流的地方。在本例中,添加了一些内容,但您也可以添加更换或移除节点。
  • 第052行可能包含您最感兴趣的内容:它返回输入的原始文本,但是应用了所有重写操作。

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71693775

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档