Javacc sample

jeffersap發表於2007-08-28

[@more@]為了讓詞法分析做得更簡單,我們通常都不會在文法分析的時候,使用“(”,“)”等字元號串來表示終結符號,而需要轉而使用LPAREN, RPAREN這樣的整型符號來表示。


PARSER_BEGIN(Grammar)
public class Grammar implements NodeType {
public ParseTreeNode GetParseTree(InputStream in) throws ParseException
{
Grammar parser =new Grammar(in);
return parser。Expression();
}
}
PARSER_END(Grammar)
SKIP :
{
" " | " " | " " | " "
}
TOKEN :
{
< ID: ["a"-"z","A"-"Z","_"> ( ["a"-"z","A"-"Z","_","0"-"9"> )* >
| < NUM: ( ["0"-"9"> )+ >
| < PLUS: "+" >
| < MINUS: "-" >
| < TIMERS: "*" >
| < OVER: "/" >
| < LPAREN: "(" >
| < RPAREN: ")" >
}

ParseTreeNode Expression() :
{
ParseTreeNode ParseTree = null;
ParseTreeNode node;
}
{
( node=Simple_Expression()
{
if(ParseTree == null)
ParseTree =node;
else
{
ParseTreeNode t;
t= ParseTree;
while(t.next != null)
t=t.next;
t.next = node;
}
}
)*
{ return ParseTree; }

}
ParseTreeNode Simple_Expression() :
{
ParseTreeNode node;
ParseTreeNode t;
int op;
}
{
node=Term(){}
(
op=addop() t=Term()
{
ParseTreeNode newNode = new ParseTreeNode();
newNode.nodetype = op;
newNode.child[0] = node;
newNode.child[1] = t;
switch(op)
{
case PlusOP:
newNode.name = "Operator: +";
break;
case MinusOP:
newNode.name = "Operator: -";
break;
}
node = newNode;
}
)*
{ return node; }
}
int addop() : {}
{
{ return PlusOP; }
| { return MinusOP; }
}
ParseTreeNode Term() :
{
ParseTreeNode node;
ParseTreeNode t;
int op;
}
{
node=Factor(){}
(
op=mulop() t=Factor()
{
ParseTreeNode newNode = new ParseTreeNode();
newNode.nodetype = op;
newNode.child[0] = node;
newNode.child[1] = t;
switch(op)
{
case TimersOP:
newNode.name = "Operator: *";
break;
case OverOP:
newNode.name = "Operator: /";
break;
}
node = newNode;
}
)*
{
return node;
}
}
int mulop() :{}
{
{ return TimersOP; }
| { return OverOP; }
}
ParseTreeNode Factor() :
{
ParseTreeNode node;
Token t;
}
{
t=
{
node=new ParseTreeNode();
node.nodetype= IDstmt;
node.name = t.image;
return node;
}
|
t=
{
node=new ParseTreeNode();
node.nodetype= NUMstmt;
node.name = t.image;
node.value= Integer.parseInt(t.image);
return node;
}
|
node=Simple_Expression()
{
return node;
}
}

其中SKIP中的定義就是在進行詞法分析的同時,忽略掉的記號。TOKEN中的,就是需要在做詞法分析的時候,識別的詞法記號。當然,這一切都是以正規表示式來表示的。
這個例子就有多個非終結符號,可以看出,我們需要為每個非終結符號寫出一個過程。不同的非終結符號的識別過程中可以互相呼叫。

以Simple_Expression()過程為例,它的產生式是Expression -> Term { addop Term },而在javacc的輸入檔案格式是,它的識別是這樣寫的node=Term(){} ( op=addop() t=Term(){ … })* 前面說過,這裡的”*”符號和正規表示式是一樣的,就是0次到無限次的重複。那麼Simple_Expression等於文法Term Addop Term Addop Term Addop Term … 而Addop也就相當於PLUS和MINUS兩個運算子號。這裡我們在寫Expression的文法的時候,同時還使用了賦值表示式,因為這個和Yacc 不同的時候,Javacc把文法識別完全地做到了函式過程中,那麼如果我們要識別Simple_Expression的文法,就相當於按順序識別Term 和Addop兩個文法,而識別那個文法,就相當於呼叫那兩個非終結符的識別函式。正是這一點,我覺得Javacc的文法識別處理上就很接近程式的操作過 程,我們不需要像YACC那樣使用嚴格的文法表示格式,複雜的系統引數了。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10230672/viewspace-966506/,如需轉載,請註明出處,否則將追究法律責任。

相關文章