javaCC学习笔记
2024-07-13 09:54:52
供稿:网友
javacc的功能和yacc相似,主要根据bnf范式生成解析程序,不过javacc是集合了词法分析和语法分析生成java解析代码,主页为:https://javacc.dev.java.net/
javacc有三个工具
javacc 用来处理语法文件(jj)生成解析代码;
jjtree 用来处理jjt文件,生成树节点代码和jj文件,然后再通过javacc生成解析代码;
jjdoc 根据jj文件生成bnf范式文档(html)
javacc使用的各种语言的grammar 文件这里有很多http://www.cobase.cs.ucla.edu/pub/javacc/ ,例如html,xml,python,vb…..,很多足够用了,呵呵。
javacc 的使用
javacc生成的文件中,最主要的是《grammar》.java这个就是解析器的主程序了了,《grammar》名由jj中定义。
现在根据例子说明jj文件的定义:
bnf范式为:
expression
::=
( ( <newline> )* simple_expression <newline> )* <eof>
simple_expression
::=
term ( addop term )*
addop
::=
<plus>
|
<minus>
term
::=
factor ( mulop factor )*
mulop
::=
<timers>
|
<over>
factor
::=
<id>
|
<num>
|
<minus>
|
<plus>
|
<lparen> simple_expression <rparen>
/*这是一个整数的四则运算的例子*/
/* 运行 javacc grammar.jj
javac *.java
java grammar
>>> 1+1*(1+1)
3
>>>^z
*/
parser_begin(grammar) /*解析代码的入口*/
public class grammar {
public static final int plusop=1;
public static final int minusop=2;
public static final int timersop=3;
public static final int overop=4;
public static void main(string args[]) throws parseexception {
grammar parser = new grammar(system.in);
parser.expression();
}
}
parser_end(grammar)
skip : /* 不处理的字符*/
{
" " | "/t"
}
token : /*生成token的字符定义*/
{
< id: ["a"-"z","a"-"z","_"] ( ["a"-"z","a"-"z","_","0"-"9"] )* >
| < num: ( ["0"-"9"] )+ >
| < plus: "+" >
| < minus: "-" >
| < timers: "*" >
| < over: "/" >
| < lparen: "(" >
| < rparen: ")" >
| <newline: ("/r/n"|"/n"|"/r")>
}
void expression() :
/*完成 expression ::=( ( <newline> )* simple_expression <newline> )* <eof> 的配陪*/
{
int value=0; /* 这个{}中是expression()的定义的局部变量*/
}
{
{
system.out.print(">>>");
}
( (<newline> /* 首先匹配newline 这个taken,完成后转到下一个解析*/
{
system.out.print(">>>"); /*在<newline>下的{}中为如果匹配到<newline>执行的java代码。*/
}
)* value= simple_expression() <newline> /*在换行之前simple_expression()解析表达式 ,输入换行后,一个预算解析完成*/
{system.out.println(value);
system.out.print(">>>");/*在<newline>下的{}中为完成表达式解析,匹配到<newline>执行的java代码。*/
}
)*
<eof> /*系统定义的taken,输入结束符*/
}
int simple_expression() :
/*完成simple_expression ::=bnf term ( addop term )*配陪 */
{
/* 这个{}中是simple_expression()的定义的局部变量*/
int value;
int tvalue;
int op;
}
{
value= term (){} /*配陪term 相*/
(
op=addop() tvalue=term()
{
switch(op)
{
case plusop:
value=value+tvalue;
break;
case minusop:
value=value - tvalue;
break;
}
}
)* /*匹配 ( addop term )* */
{ return value; }
}
int addop() : {}
{
<plus> { return plusop; }
| <minus> { return minusop; }
}
int term() :
{
int value;
int tvalue;
int op;
}
{
value=factor(){}
(
op=mulop() tvalue=factor()
{
switch(op)
{
case timersop:
value=value * tvalue;
break;
case overop:
value=value / tvalue;
break;
}
}
)*
{
return value;
}
}
int mulop() :{}
{
<timers> { return timersop; }
| <over> { return overop; }
}
int factor() :
{
int value;
token t;
}
{
t=<id> /*获得<id>的解析的值*/
{
value=100;
return value;
}
|
t=<num>
{
value= (integer.valueof(t.image)).intvalue();
return value;
}
|
t=<minus>
{
value=0-factor();
return value;
}
|
t=<plus>
{
value=factor();
return value;
}
|
<lparen> value=simple_expression() <rparen>
{
return value;
}
}
根据例子: 基本上是一个taken下跟一个{}用于处理当前tabkn的java代码
jjtree的使用:
jjtree的使用,需要根据实际情况写自己的node类,但是都必须实现node.java接口,jjtree提供一个simplenode.java的简单实现,也可以继承它,或者重写这个类。
给出一个javacc自己带例子,也是四则运算:
语法定义:
start
::=
expression ";"
expression
::=
additiveexpression
additiveexpression
::=
( multiplicativeexpression ( ( "+" | "-" ) multiplicativeexpression )* )
multiplicativeexpression
::=
( unaryexpression ( ( "*" | "/" | "%" ) unaryexpression )* )
unaryexpression
::=
"(" expression ")"
|
identifier
|
integer
identifier
::=
<identifier>
integer
::=
<integer_literal>
options {
multi=true;
visitor=true; /*实现匹配的visitor模式代码*/
node_default_void=true; /* 解析函数默认不生成node类*/
}
/*jtt 默认的生成node类名,都带ast前缀加上当前解析的语意的名称*/
parser_begin(eg4)
class eg4 {
public static void main(string args[]) {
system.out.println("reading from standard input...");
eg4 t = new eg4(system.in);
try {
aststart n = t.start();
eg4visitor v = new eg4dumpvisitor();
n.jjtaccept(v, null);
system.out.println("thank you.");
} catch (exception e) {
system.out.println("oops.");
system.out.println(e.getmessage());
e.printstacktrace();
}
}
}
parser_end(eg4)
skip :
{
" "
| "/t"
| "/n"
| "/r"
| <"//" (~["/n","/r"])* ("/n"|"/r"|"/r/n")>
| <"/*" (~["*"])* "*" (~["/"] (~["*"])* "*")* "/">
}
token : /* literals */
{
< integer_literal:
<decimal_literal> (["l","l"])?
| <hex_literal> (["l","l"])?
| <octal_literal> (["l","l"])?
>
|
< #decimal_literal: ["1"-"9"] (["0"-"9"])* >
|
< #hex_literal: "0" ["x","x"] (["0"-"9","a"-"f","a"-"f"])+ >
|
< #octal_literal: "0" (["0"-"7"])* >
}
token : /* identifiers */
{
< identifier: <letter> (<letter>|<digit>)* >
|
< #letter: ["_","a"-"z","a"-"z"] >
|
< #digit: ["0"-"9"] >
}
aststart start() #start : {} /* #start生成定义的节点类,名称为 前缀 + start.java*/
{
expression() ";"
{ return jjtthis; }
}
void expression() : {}
{
additiveexpression()
}
void additiveexpression() : {}
{
(
multiplicativeexpression() ( ( "+" | "-" ) multiplicativeexpression() )*
) #add(>1) /* add # 当满足条件(>1),add生成定义的节点类,名称为 前缀 + add.java*/
}
void multiplicativeexpression() : {}
{
(
unaryexpression() ( ( "*" | "/" | "%" ) unaryexpression() )*
) #mult(>1) /* # mult 当满足条件(>1),mult生成定义的节点类,名称为 前缀 + mult.java*/
}
void unaryexpression() : {}
{
"(" expression() ")" | identifier() | integer()
}
void identifier() #myotherid : /* # myotherid生成定义的节点类,名称为 前缀 + myotherid.java*/
{
token t;
}
{
t=<identifier>
{
jjtthis.setname(t.image);
}
}
void integer() #integer : {} /* # integer生成定义的节点类,名称为 前缀 + integer.java*/
{
<integer_literal>
}
jjdoc的使用很简单 。
如果需要生成其它语言的(例如c#)解析器,除了处理c的yacc和lex外,antlr(http://www.antlr.org/)也是一个不错的选择。