首图Javacc语法格式文件
JavaCC的语法描述文件格式如下所示:
options { JavaCC的选项}PARSER_BEGIN(解析器类名)package 包名;import 库名;public class 解析器类名 { 任意的Java代码}PARSER_END(解析器类名)扫描器的描述解析器的描述
JavaCC和java一样将解析器的内容 定义在单个类中 ,因此会在PARSER_BEGIN和PARSER_END之间描述这个类的相关内容。
下面拿一段实际代码来做示例,并对代码进行逐段拆分解析。
demo代码及含义解释
代码结构解析:
- 1. options块中将STATIC选项设置为false, 将该选项设置为true的话JavaCC生成的所有成员及方法都将被定义为static,若将STATIC设置为true则所生成的解析器无法在多线程环境下使用,因此该选项总是被设置为false。(STATIC的默认值为true)
- 2. 从PARSER_BEING(Adder)到PARSER_END(Adder)是解析器类的定义。解析器类中需要定义的成员和方法也写在这里。为了实现即使只有Adder类也能够运行,这里定义了main函数。
- 3. 之后的SKIP和TOKEN部分定义了扫描器。SKIP表示要跳过空格、制表符(tab)和换行符。TOKEN表示扫描整数字符并生成token。
- 4. long expr...开始到最后的部分定义了狭义的解析器。这部分解析token序列并执行某些操作。
main函数代码解析:
main函数将所有命令行参数的字符串作为计算对象的算式,依次用evaluate方法进行计算。evaluate方法中生成了Adder类的对象实例 。并让Adder对象来计算(解析)参数字符串src。要运行JavaCC生成的解析器类,需要下面2个步骤:
第1点:JavaCC4.0和JavaCC5.0生成的解析器中默认定义有如下四种类型的构造函数。
- • Parser(InputStream s):第1种的构造函数是通过传入InputStream对象来构造解析的。这个构造函数无法设定输入字符串的编码,因此无法处理中文字符等。
- • Parser (InputStream s, String encoding):第2种的构造函数除了InputStream对象外,还可以设置输入字符串的编码来生成解析器。但如果要解析中文字符串或注释的话,就必须使用第2种/3种构造函数。
- • Parser(Reader r):第3种的构造函数用于解析Reader对象所读入的内容。
- • Parser (x x x x TokenManager tm):第4种是将扫描器作为参数传入。
解析器生成后,用这个实例调用和需要解析的语法同名的方法。这里调用Adder对象的expr方法,接回开始解析,解析正常结束后会返回语义值。
options { STATIC = false;}PARSER_BEGIN(Adder)package com.susu.testJavaCC;import java.io.*;public class Adder { public static void main(String[] args) { for (String arg : args) { try { System.out.println(evaluate(arg));// return(evaluate(arg)); } catch (ParseException ex) { System.err.println(ex.getMessage()); } } } public static long evaluate(String src) throws ParseException { Reader reader = new StringReader(src); return new Adder(reader).expr(); }}PARSER_END(Adder)SKIP: { <[" ", "\t", "\r", "\n"]> }TOKEN: { <INTEGER: (["0"-"9"])+>}long expr():{ Token x, y;}{ x=<INTEGER> "+" y=<INTEGER> <EOF> { return Long.parseLong(x.image) + Long.parseLong(y.image); }}
demo代码注释
// 设置选项,生成的parser类是否是静态的options { STATIC = false; // 设置为false,意味着生成的parser类不是静态的}// 定义解析器的开始,指定了解析器的类名PARSER_BEGIN(Adder)// 定义了包名和导入的类package com.susu.testJavaCC;import java.io.*;// 定义了解析器的主体类public class Adder { // 主函数,用于执行程序 public static void main(String[] args) { // 遍历命令行参数 for (String arg : args) { try { // 尝试计算表达式的值并打印结果 System.out.println(evaluate(arg)); // return(evaluate(arg)); // 这行被注释掉了,如果取消注释,将返回计算结果并退出 } catch (ParseException ex) { // 如果解析过程中出现错误,打印错误信息 System.err.println(ex.getMessage()); } } } // 一个公共的静态方法,用于计算传入字符串表达式的值 public static long evaluate(String src) throws ParseException { // 创建一个字符串读取器 Reader reader = new StringReader(src); // 使用读取器作为输入,调用expr非终端符号,并返回结果 return new Adder(reader).expr(); }}// 定义解析器的结束PARSER_END(Adder)// 定义跳过的符号,即在解析过程中要忽略的字符,这里是空格、制表符、回车和换行符SKIP: { <[" ", "\t", "\r", "\n"]> // 这些字符在解析过程中会被忽略}// 定义一个TOKEN,即一个词法单元,这里定义了一个名为INTEGER的词法单元TOKEN: { <INTEGER: (["0"-"9"])+> // 匹配一个或多个数字,构成一个整数}// 定义一个非终端符号expr,它用于解析加法表达式,并返回计算结果long expr():{ Token x, y;}{ x=<INTEGER> "+" y=<INTEGER> <EOF> { // 当解析到两个由空格分隔的整数和一个加号时,计算这两个整数的和 // Token是javacc定义的一个类,用于表示词法单元 // image属性是Token匹配到的字符串 return Long.parseLong(x.image) + Long.parseLong(y.image); }}
参考文章
[1]. 3.JavaCC 语法描述文件的格式解析