我有一个分配使用的JavaCC,以与语义分析自上而下的解析器由讲师提供的语言。 我已经写了生产规则和没有错误。 我完全被卡住了如何使用JJTree会为我的代码和我的时间淘换教程互联网已经没有得到我到任何地方的。 只是想知道任何人都可以花一些时间来解释如何在代码中实现JJTree的? 或者,如果有一个隐藏的一步一步的教程就在某个地方,这将是一个很大的帮助!
下面是一些在他们帮助的情况下我的生产规则。 提前致谢!
void program() : {}
{
(decl())* (function())* main_prog()
}
void decl() #void : {}
{
(
var_decl() | const_decl()
)
}
void var_decl() #void : {}
{
<VAR> ident_list() <COLON> type()
(<COMMA> ident_list() <COLON> type())* <SEMIC>
}
void const_decl() #void : {}
{
<CONSTANT> identifier() <COLON> type() <EQUAL> expression()
( <COMMA> identifier() <COLON> type() <EQUAL > expression())* <SEMIC>
}
void function() #void : {}
{
type() identifier() <LBR> param_list() <RBR>
<CBL>
(decl())*
(statement() <SEMIC> )*
returnRule() (expression() | {} )<SEMIC>
<CBR>
}
创建使用JavaCC中的AST看起来像创建一个“正常”的解析器(在定义了很多jj
文件)。 如果你已经有一个工作的语法,它的(相对)容易:)
下面是创建一个AST所需的步骤:
- 重命名您的
jj
语法文件jjt
- 同根标签的 装饰它(斜体字是我自己的术语......)
- 调用
jjtree
您jjt
语法,这将产生一个jj
为你的文件 - 调用
javacc
您产生jj
语法 - 编译生成
java
源文件 - 测试
下面是一个简单的一步一步的教程,假设你使用的MacOS或* nix中,有javacc.jar
在同一个目录下的文件为你的语法文件(S)和java
和javac
是您系统的PATH:
1
假设你的jj
语法文件被称为TestParser.jj
,其重命名为:
mv TestParser.jj TestParser.jjt
2
现在棘手的问题: 装饰你的语法,以便在创建正确的AST结构。 您可以通过添加装饰的AST(或节点,或生产规则(都一样)) #
后跟标识符之后(和之前的:
)。 在你原来的问题,你有很多的#void
不同的制作,这意味着你要对不同的生产规则创建同一类型的AST的的:这是不是你想要的。
如果你不装饰你的生产,生产的名称作为节点(这样,你可以删除的类型#void
):
void decl() :
{}
{
var_decl()
| const_decl()
}
现在,规则简单地返回任何AST规则var_decl()
或const_decl()
返回。
现在让我们看看(简体) var_decl
规则:
void var_decl() #VAR :
{}
{
<VAR> id() <COL> id() <EQ> expr() <SCOL>
}
void id() #ID :
{}
{
<ID>
}
void expr() #EXPR :
{}
{
<ID>
}
我饰有#VAR
类型。 现在,这意味着该规则将返回下面的树结构:
VAR
/ | \
/ | \
ID ID EXPR
正如你所看到的,终端从AST丢弃! 这也意味着,在id
和expr
规则失去它们的文本<ID>
终端匹配。 当然,这是不是你想要的。 对于需要保持内部文本终端相匹配的规则,则需要明确设置.value
树到的.image
匹配的终端:
void id() #ID :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void expr() #EXPR :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
使输入"var x : int = i;"
看起来是这样的:
VAR
|
.---+------.
/ | \
/ | \
ID["x"] ID["int"] EXPR["i"]
这是你如何为您的AST一个适当的结构。 下面跟随小的语法这是你自己的语法,包括一个小的一个非常简单的版本, main
以测试它的所有方法:
// TestParser.jjt
PARSER_BEGIN(TestParser)
public class TestParser {
public static void main(String[] args) throws ParseException {
TestParser parser = new TestParser(new java.io.StringReader(args[0]));
SimpleNode root = parser.program();
root.dump("");
}
}
PARSER_END(TestParser)
TOKEN :
{
< OPAR : "(" >
| < CPAR : ")" >
| < OBR : "{" >
| < CBR : "}" >
| < COL : ":" >
| < SCOL : ";" >
| < COMMA : "," >
| < VAR : "var" >
| < EQ : "=" >
| < CONST : "const" >
| < ID : ("_" | <LETTER>) ("_" | <ALPHANUM>)* >
}
TOKEN :
{
< #DIGIT : ["0"-"9"] >
| < #LETTER : ["a"-"z","A"-"Z"] >
| < #ALPHANUM : <LETTER> | <DIGIT> >
}
SKIP : { " " | "\t" | "\r" | "\n" }
SimpleNode program() #PROGRAM :
{}
{
(decl())* (function())* <EOF> {return jjtThis;}
}
void decl() :
{}
{
var_decl()
| const_decl()
}
void var_decl() #VAR :
{}
{
<VAR> id() <COL> id() <EQ> expr() <SCOL>
}
void const_decl() #CONST :
{}
{
<CONST> id() <COL> id() <EQ> expr() <SCOL>
}
void function() #FUNCTION :
{}
{
type() id() <OPAR> params() <CPAR> <OBR> /* ... */ <CBR>
}
void type() #TYPE :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void id() #ID :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void params() #PARAMS :
{}
{
(param() (<COMMA> param())*)?
}
void param() #PARAM :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void expr() #EXPR :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
3
让jjtree
类(包含在javacc.jar
)创建一个jj
为您的文件:
java -cp javacc.jar jjtree TestParser.jjt
4
先前的步骤已创建的文件TestParser.jj
(如果一切正常)。 让javacc
(也存在于javacc.jar
)过程吧:
java -cp javacc.jar javacc TestParser.jj
五
编译所有的源文件,这样做:
javac -cp .:javacc.jar *.java
(在Windows上,做: javac -cp .;javacc.jar *.java
)
6
真理的时刻已经到来:让我们看看是否一切实际工作! 为了让解析器处理输入:
var n : int = I;
const x : bool = B;
double f(a,b,c)
{
}
执行以下命令:
java -cp . TestParser "var n : int = I; const x : bool = B; double f(a,b,c) { }"
你应该看到以下被打印在控制台上:
PROGRAM
decl
VAR
ID
ID
EXPR
decl
CONST
ID
ID
EXPR
FUNCTION
TYPE
ID
PARAMS
PARAM
PARAM
PARAM
请注意,您没有看到文本ID
的匹配,但相信我,他们在那里。 该方法dump()
根本不显示它。
HTH
编辑
对于工作语法,包括表情,你可以看看我的下面的表达式求值: https://github.com/bkiers/Curta (语法是src/grammar
)。 你可能想看看如何在二元表达式的情况下创建根节点。
下面是一个使用JJTree的一个例子http://anandsekar.github.io/writing-an-interpretter-using-javacc/