JavaCC的基本使用

扯一下.jj文件的构成与基本语法

主要由四部分构成

  1. options{}部分:这个部分对产生的语法分析器的特性进行说明,例如向前看的token的个数(用来解除冲突)。这一部分是可以省略的,因为每一个选项都有默认值,当我们没有对某个选项进行说明时,它就采用默认值。也可以把这些选项作为javacc命令的参数来启动javacc,可以达到同样的效果。

    options
    {
      static = true;
    }
  2. 解析器类的声明:这个部分指定了分析器类的名字,以及其他类中成员的声明。这个部分是必须有的。这个部分的声明如下:

    PARSER_BEGIN(MyNewGrammar)
    package com.pngfi.test;//指定生成代码存放的包名
    
    public class MyNewGrammar
    {
      public static void main(String args []) throws ParseException
      {
        MyNewGrammar parser = new MyNewGrammar(System.in);
        while (true)
        {
          System.out.println("Reading from standard input...");
          System.out.print("Enter an expression like \"1+(2+3)*4;\" :");
          try
          {
            switch (MyNewGrammar.one_line())
            {
              case 0 : System.out.println("OK.");
              break;
              case 1 : System.out.println("Goodbye.");
              break;
              default : break;
            }
          }
          catch (Exception e)
          {
            System.out.println("NOK.");
            System.out.println(e.getMessage());
            MyNewGrammar.ReInit(System.in);
          }
          catch (Error e)
          {
            System.out.println("Oops.");
            System.out.println(e.getMessage());
            break;
          }
        }
      }
    }
    
    PARSER_END(MyNewGrammar)
  3. 词法部分声明:这里面有四类:SKIP、TOKEN、SPECIAL_TOKEN、MORE。其中,SKIP用来说明被忽略的串,下面是一个例子:

    SKIP :
    {
      " "
    | "\r"
    | "\t"
    | "\n"
    }

    TOKEN用来说明在词法层次上识别的token,下面是一个例子:

    TOKEN : /* OPERATORS */
    {
      < PLUS : "+" >
    | < MINUS : "-" >
    | < MULTIPLY : "*" >
    | < DIVIDE : "/" >
    }
    
    TOKEN :
    {
      < CONSTANT : (< DIGIT >)+ >
    | < #DIGIT : [ "0"-"9" ] >
    }
    

    在词法声明部分,以#开头的token只是在词法分析时使用,不能作为语法分析的输入,也就是说,它相对词法分析是局部的。

  4. 语法声明和动作代码:这一部分生成的代码会直接插入解析器类声明的结束括号之前。一般而言,语法中的每一个非终结符都对应一个规则,每一个规则都对应生成代码中的一个函数,其中规则的形式如下:

    返回值类型 规则名():
    {
    变量声明和一些初始化的动作
    }
    {
    上下文无关文法的右部分,其中每个组成部分的形式如下:
    语法部分 {动作部分}
    }
    两个部分都可以省略。语法部分可以是一个字符串(简单的token常常可以这样处理),TOKEN中声明的token,或一个对某个非终结符对应的函数的调用。
    JavaCC模板文件中自带的代码例子如下:

    int one_line() :
    {}
    {
      sum() ";"
      {
        return 0;
      }
    | ";"
      {
        return 1;
      }
    }
    
    void sum() :
    {}
    {
      term()
      (
        (
          < PLUS >
        | < MINUS >
        )
        term()
      )*
    }
    
    void term() :
    {}
    {
      unary()
      (
        (
          < MULTIPLY >
        | < DIVIDE >
        )
        unary()
      )*
    }
    
    void unary() :
    {}
    {
      < MINUS > element()
    | element()
    }
    
    void element() :
    {}
    {
      < CONSTANT >
    | "(" sum() ")"
    }

说完构成,说一下基本语法

  1. []:在词法(Token)部分表示其中的项中选择一个,在语法部分表示的内容是可选的

    例如下面DIGIT表示0-9中的任意一个数字

     TOKEN : /* 定义整数 和实数*/
    {
      < DIGIT : [ "0"-"9" ]>
      }

    在规则中表示匹配一个数字后面可以有分号,也可以没有分号

    void sum() :
    {}
    {
      < DIGIT > [ ; ]
    }
  2. +:前面的内容出现一次或多次。
    表示匹配至少一个连续的数字,如99999,6,045等

    void sum() :
    {}
    {
        (< DIGIT >)+ 
    }
  3. *: 前面的内容出现0次或多次。

  4. ?:前面的内容出现0次或一次。
    这两个和上面的类似

  5. -:前后构成的闭区间。

  6. ~:后面的内容的补。
  7. |:或者
    匹配阿拉伯字母大小写的任意一个

    TOKEN : /* 定义整数 和实数*/
    {
      < LETTER: [ "a"-"z"|"A"-"Z" ]>
      }
  8. ():改变运算的优先级,把其中的内容作为一个整体。

    表示匹配0个或多个数字字母的集合。比如1a2b3a4t, ,6y,7u8o等等

    void sum() :
    {}
    {
        (< DIGIT >< LETTER >)* 
    }

你可能感兴趣的:(编译原理)