【问题标题】:ANTLR 4: Parsing grammarANTLR 4:解析语法
【发布时间】:2016-11-09 00:50:52
【问题描述】:

我想从 AppleSoft Basic 脚本中解析一些数据。 我选择ANTLR并下载这个语法:jvmBasic

我正在尝试提取不带参数的函数名称:

return parser.prog().line(0).amprstmt(0).statement().getText();

但它返回 PRINT"HELLO" 例如除行号外的完整表达式 这是我要解析的字符串:

10 打印“你好!”

【问题讨论】:

  • 注意:全班是:link
  • malarzm,感谢编辑

标签: java antlr4


【解决方案1】:

我认为这个问题确实取决于您的 ANTLR 程序实现,但如果您使用的是 treewalker/listener,您可能希望针对特定标记的规则而不是整个“语句”规则,该规则是循环的并且包含多种类型声明:

//each line can have one to many amprstmt's
line
   : (linenumber ((amprstmt (COLON amprstmt?)*) | (COMMENT | REM)))
   ;

amprstmt
   : (amperoper? statement) //encounters a statement here
   | (COMMENT | REM)
   ;
//statements can be made of 1 to many sub statements
statement
   : (CLS | LOAD | SAVE | TRACE | NOTRACE | FLASH | INVERSE | GR | NORMAL | SHLOAD | CLEAR | RUN | STOP | TEXT | HOME | HGR | HGR2)
   | prstmt
   | printstmt1 //the print rule
   //MANY MANY OTHER RULES HERE TOO LONG TO PASTE........
   ;
//the example rule that occurs when the token's "print" is encountered
printstmt1
   : (PRINT | QUESTION) printlist?
   ;

printlist
   : expression (COMMA | SEMICOLON)? printlist*
   ;

从这里的 BNF 类型语法中可以看出,该语法中的语句规则包括 print 语句以及所有其他类型语句的规则,因此它将包含 10、PRINT 和 hello,然后返回带有getText() 方法,当您遇到任何这些情况时,除了 linenumber 之外的所有内容,它是语句规则之外的规则。

如果您想针对这些特定规则来处理遇到它们时发生的情况,您很可能希望通过扩展 jvmBasiListener 类来为 ANTLR 为每个规则生成的每个方法添加功能,如图所示 here

示例:

-jvmBasicListener.java
-extended to jvmBasicCustomListener.java

void enterPrintstmt1(jvmBasicParser.Printstmt1Context ctx){
System.out.println(ctx.getText());
}

但是,如果所有这些都已设置,并且您只想使用单行返回字符串值等,那么尝试通过寻址语句的子节点来访问较低级别的方法可能会起作用 amprstmt->statement- >printstmt1->值:

 return  parser.prog().line().amprstmt(0).statement().printstmt1().getText();

只是为了稍微缩小我的回答范围,专门针对您的输入“10 PRINT "HELLO"”的规则是:

linenumber (contains Number) , statement->printstmt1 and statement->datastmt->datum (contains STRINGLITERAL)

因此,如上所示,linenumber 规则是独立存在的,而定义文本的其他 2 个规则是语句的子级,这解释了在获取语句规则文本时输出除行号之外的所有内容。

解决每一个问题并使用 getText() 而不是诸如语句之类的包含规则可能会给您想要的结果。


我会更新以解决您的问题,因为答案可能会稍长一些,我认为处理特定规则而不是生成侦听器或访问者的最简单方法是在您的语法文件规则中执行如下操作:

printstmt1
   : (PRINT | QUESTION) printlist? {System.out.println("Print"); //your java code }
   ;

这只会让您处理每个规则并执行您希望执行的任何 java 操作。然后,您可以简单地使用以下代码编译代码:

java -jar antlr-4.5.3-complete.jar jvmBasic.g4 -visitor

在此之后,您可以随心所欲地运行您的代码,这是一个示例:

import JVM1.jvmBasicLexer;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;


public class Jvm extends jvmBasicBaseVisitor<Object> {


    public static void main(String[] args) {
        jvmBasicLexer lexer = new jvmBasicLexer(new ANTLRInputStream("10 PRINT \"Hello!\""));
        jvmBasicParser parser = new jvmBasicParser(new CommonTokenStream(lexer));
        ParseTree tree = parser.prog();
    }

}

这个例子的输出就是:

Print

您还可以在语法中合并任何您喜欢的 Java 方法来处理遇到的每个规则,并开发您自己的类和方法来处理它或直接打印出结果。


更新

现在只是为了解决最新的问题: parser.line().linenumber().getText() - 对于行号,因为行不是语句的一部分

parser.prog().line(0).amprstmt(0).statement().printstmt1().PR‌​INT().getText() - 用于 PRINT,因为它在 printstmt1 中被隔离,但不包括规则中的 CLR

parser.prog().line(0).amprstmt(0).statement().printstmt1().pr‌intlist().expression().getText() - 获取值“hello”,因为它是 printstmt1 规则中包含的表达式的一部分。

:) 祝你好运

【讨论】:

  • 谢谢,但我可以只使用 antlr4 生成器(不实现自己的监听器)只解析一个语句吗?
  • 我更新了答案以解决您的问题,因为评论太长了,想举个例子,希望对您有所帮助。
  • 谢谢 :) 这很有帮助。
  • Dean219,我尝试使用 -visitor 参数编译语法,但出现错误。这是截图:yadi.sk/i/ToBZcgjTt7XFr
  • 好的,有很多错误可能首先是由于包的排列。在我使用 antlr 的 jar 编译我的语法后,我通常将所有文件复制到 netbeans 中,并且必须手动更正生成的每个文件顶部的包名称,但是在屏幕截图中,您当前打开的文件的包不是无效或由于某种原因加下划线。最可能的原因是您正在实现或扩展的包中的一个类没有被更改。尝试进入 antlr 生成的所有文件并确保它们都在包 com.monobogdan.compler 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-06
  • 2011-01-30
  • 1970-01-01
  • 2014-05-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多