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/)也是一个不错的选择。