如何在语法上实现JJTree

时间:2012-12-16 14:29:36

标签: java parsing compiler-construction abstract-syntax-tree javacc

我有一项任务是使用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>
}

2 个答案:

答案 0 :(得分:40)

使用JavaCC创建AST看起来很像创建“普通”解析器(在jj文件中定义)。如果你已经有了一个有效的语法,那就相当容易了:)

以下是创建AST所需的步骤:

  1. 将您的jj语法文件重命名为jjt
  2. root-labels 装饰(斜体字是我自己的术语......)
  3. 在您的jjtree语法上调用jjt,这将为您生成jj个文件
  4. 根据您生成的javacc语法
  5. 调用jj
  6. 编译生成的java源文件
  7. 测试
  8. 这是一个快速的分步教程,假设您正在使用MacOS或* nix,将javacc.jar文件放在与语法文件相同的目录中,并java和{ {1}}在您系统的路径中:

    1

    假设您的javac语法文件名为jj,请将其重命名为:

    TestParser.jj

    2

    现在是棘手的部分:装饰你的语法,以便创建正确的AST结构。您装饰 AST(或节点或生产规则(完全相同)),方法是添加mv TestParser.jj TestParser.jjt 后跟一个标识符(在#之前)。在您的原始问题中,您在不同的制作中有很多:,这意味着您为不同的制作规则创建了相同类型的AST:这不是您想要的。

    如果您没有装饰您的作品,则将作品的名称用作节点的类型(因此,您可以删除#void):

    #void

    现在规则只返回规则void decl() : {} { var_decl() | const_decl() } var_decl()返回的任何AST。

    现在让我们看一下(简化的)const_decl()规则:

    var_decl

    我用void var_decl() #VAR : {} { <VAR> id() <COL> id() <EQ> expr() <SCOL> } void id() #ID : {} { <ID> } void expr() #EXPR : {} { <ID> } 类型装饰。现在这意味着此规则将返回以下树结构:

    #VAR

    如您所见,终端从AST中被丢弃!这也意味着 VAR / | \ / | \ ID ID EXPR 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;"

    这是为AST创建适当结构的方法。下面是一个小语法,它是你自己语法的一个非常简单的版本,包括一个小的 VAR | .---+------. / | \ / | \ ID["x"] ID["int"] EXPR["i"] 方法来测试它:

    main

    3

    // 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;} } 类(包含在jjtree中)为您创建一个javacc.jar文件:

    jj

    4

    上一步创建了文件java -cp javacc.jar jjtree TestParser.jjt (如果一切正常)。让TestParser.jj(也出现在javacc中)处理它:

    javacc.jar

    5

    要编译所有源文件,请执行以下操作:

    java -cp javacc.jar javacc TestParser.jj
    

    (在Windows上,执行:javac -cp .:javacc.jar *.java

    6

    真相的时刻到了:让我们看看一切是否真的有效!让解析器处理输入:

    javac -cp .;javacc.jar *.java

    执行以下操作:

    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
    

    请注意,您没有看到java -cp . TestParser "var n : int = I; const x : bool = B; double f(a,b,c) { }" 匹配的文字,但请相信我,他们就在那里。方法ID根本没有显示它。

    HTH

    修改

    对于包含表达式的工作语法,您可以查看我的以下表达式求值程序:https://github.com/bkiers/Curta(语法在dump()中)。您可能希望了解如何在二进制表达式的情况下创建根节点。

答案 1 :(得分:1)