JAVACC使用总结(五):语法进阶-集合函数递归脚本

集合函数

我记得应该在初中时候学习集合及集合的相关运算的概念,现在在看看下面的总结复习一下。

1、集合的含义:某些指定的对象集在一起就成为一个集合,其中每一个对象叫元素。

2、集合的中元素的三个特性: 1)元素的确定性; 2)元素的互异性;3)元素的无序性

列如:集合{1,4,7} 与{1,4,7,7}、{7,4,1}、{4,7,1}是等价的相同的集合

集合的定义其实就跟java中Set集合实现几乎是一样。

再看看下表中的集合操作

操作符号例子
并(union){1, 2}∪{红色, 白色} = {1, 2, 红色, 白色}
交(intersect)

{1, 2}∩{红色, 白色} ={}

{1, 2, 绿色}∩{红色, 白色, 绿色} = {绿色}

差(subtract)-

{1, 2}−{红色, 白色} = {1, 2}

{1, 2, 绿色}−{红色, 白色, 绿色} = {1, 2}

{1, 2} -{1,2, 白色} ={}

示例: (A∩B)∩C = A∩(B∩C)

集合操作运算的结果还是一个集合,并且还可以参与接下来的集合运算,这就有点类似函数递归调用的感觉。这就是这边文章的核心,我们要通过javacc实现一套集合函数,包括集合的运算(可以递归),通过这个脚本编写拔高自己在javacc语法定义及动作实现水平。

当然为了体现函数的定义及递归的特点,集合的操作不在用数学上定义的符文(这类符号不好打出来)而是采用“函数名(参数1,参数2...)”方式体现。

例如:subtract(u3,intersect(union(u1,u2),u3))

集合的定义还是“{1,2,'red' }”方式体现。既然是脚本,那么变量赋值定义也是少不了的。我们就仿照python这类的脚本变量去搞,简单一些。

例如1: a = 1+5*(7-3)

例如2:u1 = {1,4,7+9,'red'}

语法分析

 在我总结的四则运算解析的那边文章里,提到了语法分析的方法是画语法解析树图。这次还是通过画图讲解。不过只画出集合定义以及集合操作函数相关的解析树图,这一部分处理好就可以把这个棵子树合并到整个解析树中。这也是分治的思想。

 集合定义语法树

集合里是有0到n元素e 构成的,元素e 又是有数字四则(也是语法树)或者 '字符串'(普通字符语法树) 构成的。所以就形成了上图的集合定义语法树图。

集合操作函数语法树

funName:union、intersect、subtract

集合操作函数的参数是至少2个set集合构成的,set集合有可以是集合定义树,或者是集合操作(运算后的结果仍然是个集合),所以就形成了循环嵌套的语法树图。

看起来这个树跟之前的四则运算有点像,实现上是有区别的。四则运算操作数和结果都是数字(double),但是集合操作函数的参数set从图上看是两个不同的类型。考验抽象定义的能力到了,可以定义个名字叫算子(xxOperator)接口 ,集合的定义和集合操作函数是该接口的实现类,那么就在形式上是同一的类型了。

好了核心点的分析就到此为止了,其他的赋值表达式定义等语法树相对比较简单,在此不在画图分析。

脚本语法实现

Zcode.jj

options{
     STATIC = false;
     JDK_VERSION = "1.8";

}
PARSER_BEGIN(Zcode)
package com.javacc.zcode;
import com.javacc.zcode.*;
import com.javacc.zcode.operator.*;
import com.javacc.zcode.operator.bean.*;
import java.io.StringReader;
import java.util.*;

public class Zcode {
    ZpContext zpContext;
    public Zcode(String expr){
         this(new StringReader(expr));
         zpContext = new ZpContext();
    }

}


PARSER_END(Zcode)


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

TOKEN : {
    <NUMBER : <DIGITS>
      | <DIGITS> "." <DIGITS>
     >
  |
    <#DIGITS :(["0"-"9"])+>
}

TOKEN : {
     < LPAREN: "(" >
   | < RPAREN: ")" >
   | < LBPAREN: "{">
   | < RBPAREN: "}">
   | < ADD : "+" >
   | < SUBTRACT : "-" >
   | < MULTIPLY : "*" >
   | < DIVIDE : "/" >
   | < EQUALS : "=">
   | < COMMA : ",">
   | < SEMIC : ";">
   | < SINGLE_QUOT : "'">

   |< UNION : "UNION" | "union">
   |< INTERSECT : "INTERSECT"| "intersect">
   |< SET_SUB : "SUBTRACT" | "subtract">

   |<PRINT : "PRINT" | "print">
   |<PRINTLN : "PRINTLN" | "println">

}
TOKEN : { < EOL : "\n" | "\r" | "\r\n" > }

TOKEN : {
<ID : <LETTER> (<DIGITS>|<LETTER>)*>
}



TOKEN : { < # LETTER : (["_",
                         "~",
                         "a"-"z","A"-"Z"])+ > }
//脚本解析入口
void  parse():
{

 }
{
  (
      LOOKAHEAD(2,<ID> <EQUALS>)
       assignExp() <SEMIC>  //赋值表达式
    | printFunction() <SEMIC> //打印函数
  )+ //脚本中包含多条赋值表达和打印函数,每条都以“;”结尾 作为区分


}
//打印函数
void printFunction():
{
  int flag;
  //打印目标
  Object target = null;
  Token vt;
}
{
    (
      <PRINT> {flag=1;}
    | <PRINTLN>{flag=2;}
    )
      <LPAREN>
      (
       vt = <ID> {target = zpContext.getVariable(vt.image);} //变量
       | target = calc() //四则运算
       | target = defineSets() //集合定义
       | target = setOperFunciton() //集合运算函数
      )
      <RPAREN>
    {
        PrintOperator print = new PrintOperator(flag,target);
        print.operator();
     }
}

//赋值表达式
void assignExp():
{
   Token t;
   double v;
   ZcOperator operator = null;
}
{
   t=<ID>
        <EQUALS>
      (
         v = calc() {zpContext.putVariable(t.image,v);}
       | operator = setOperFunciton() {zpContext.putVariable(t.image,operator);}
       | operator = defineSets() {zpContext.putVariable(t.image,operator);}
      )
 }
//集合定义:{element,element.....} 或者{}
ZcOperator defineSets():
{
    Set data = new LinkedHashSet();
    ZcResult elem = null;
}
{
   <LBPAREN>
    (
       elem = element() {data.add(elem.getData());}
       (<COMMA>
         elem = element() {data.add(elem.getData());}
       )*
    )*
   <RBPAREN>
   { return new SetOperator(data);}
}
//集合元素定义
ZcResult element():
{
  Token t;
  double v;
}
{
   v = calc() {return new ZcResult(v);}   //匹配数字四则
  | <SINGLE_QUOT> t =  <ID> <SINGLE_QUOT> {return new ZcResult(t.image);} //匹配'字符串'
}


//解析集合操作函数
ZcOperator setOperFunciton():
{
  int flag;
  List<ZcOperator> pList = new ArrayList<ZcOperator>();
  ZcOperator operator = null;
}
{
  (<UNION> {flag=1;}
   |<INTERSECT> {flag =2;}
   |<SET_SUB> {flag =3;}
  )
     <LPAREN>
          operator = setOperBase() {pList.add(operator);} //操作集合
          (
            <COMMA>
            operator = setOperBase() {pList.add(operator);}
          )+
     <RPAREN>

     { return new SetFunctionOperator(flag,pList,zpContext); }
}
//集合
ZcOperator setOperBase():
{
    ZcOperator operator = null;
    Token t;
}
{
       operator = defineSets() {return operator;} //集合定义
   |   t = <ID> { return zpContext.getVariable(t.image);} //集合赋值给的变量
   |   operator = setOperFunciton() {return operator;} //集合操作函数
}

//解析一级树处理加减
double calc():
{
 double left;
 double right;

}
{
  left = mutlOrDiv()


  (<ADD>   right = mutlOrDiv() {left += right;}
    | <SUBTRACT> right = mutlOrDiv() {left = left - right;}
     )*

  {
     return left;
   }
}

//解析二级树处理乘除
double mutlOrDiv():
{
 double left;
 double right;

}
{
  left = parseBase()
  (<MULTIPLY> right = parseBase() {left *= right ;}
    | <DIVIDE> right = parseBase() {left = left/right;}
     )*
  {
    return left;
   }
}

//解析三级树
double parseBase() :
{
 Token t = null;
 double num;
}
{
  t = <NUMBER> {return Double.parseDouble(t.image);}
//处理括号里的四则
 | <LPAREN> num = calc() <RPAREN> {return num;}

}


该语法文件是在四则运算基础进行的扩展的,其核心实现基本上与语法分析的思路基本是一致,不同的地方是增加对变量的解析支持,另外增加了赋值表达式、打印函数的解析等。

ZcOperator.java

/**
 * @description: 算子定义接口
 * 
 */
public interface ZcOperator<T> {

    /**
     * 算子运算操作
     * @return ZcResult 返回结果
     */
    ZcResult<T> operator();

}

 ZcResult.java

/**
 * @description: 算子运算的结果类
 */
public class ZcResult <T>{
    private final T data;

    public ZcResult(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

}

ZcVoid.java

/**
 * @description: 无实际返回结果的算子结果类
 */
public class ZcVoid  extends ZcResult<ZcVoid>{
    private final static  ZcVoid zcVoid = new ZcVoid();

    public ZcVoid(ZcVoid data) {
        super(data);
    }

    private ZcVoid(){
        super(zcVoid);
    }

    @Override
    public ZcVoid getData() {
        throw new  UnsupportedOperationException("ZcVoid Unsupported");
    }

    public static ZcVoid getInstance(){
        return zcVoid;
    }
}

SetOperator.java

/**
 * @description: 集合定义算子
 */
public class SetOperator implements ZcOperator<Set>{

    /**
     * 结果集
     */
    private ZcResult<Set> result;

    public SetOperator(Set data){
        result = new ZcResult<Set>(data);
    }

    public ZcResult<Set> operator() {
        return result;
    }
}

SetFunctionOperator.java

/**
 * @description: 集合操作函数算子
 */
public class SetFunctionOperator implements ZcOperator<Set>{
    /**
     * 函数名标记
     */
    private final int flag;

    /**
     * 函数中的参数列表
     */
    private List<ZcOperator> params;

    /**
     * 运算上下文
     */
    private ZpContext context;

    public SetFunctionOperator(int flag, List<ZcOperator> params, ZpContext context) {
        this.flag = flag;
        this.params = params;
        this.context = context;
    }

    public ZcResult<Set> operator() {
       List<Set> list = new ArrayList<Set>();

       //先把参数列表中结果拿出来放到list数组中
        for (ZcOperator op : params){
            //从{e,...e}集合定义中获取
            if(op instanceof SetOperator){
                SetOperator setOperator = (SetOperator) op;
                list.add(setOperator.operator().getData());
            }
            //从集合的操作运算中获取结果
            if(op instanceof SetFunctionOperator){
                SetFunctionOperator sfp = (SetFunctionOperator) op;
                //注意sfp.operator()会进行递归运算
                list.add(sfp.operator().getData());
            }
        }


        Set set = new LinkedHashSet();
        //利用hutool工具包中集合工具进行集合运算操作
        switch (flag){
            //进行union(并)操作
            case 1:
                if (list.size()>2){
                    set =  CollUtil.unionDistinct(list.get(0),list.get(1),
                            list.subList(2,list.size()).toArray(new Set[0]));
                }else {
                    set = CollUtil.unionDistinct(list.get(0),list.get(1));
                }
                break;
            //进行intersection(交)操作
            case 2:
                if (list.size()>2){
                    set = CollUtil.intersectionDistinct(list.get(0),list.get(1),
                            list.subList(2,list.size()).toArray(new Set[0]));
                }else {
                    set = CollUtil.intersectionDistinct(list.get(0),list.get(1));
                }
                break;
            //进行subtract(差)操作
            case 3:
               set = (Set) CollUtil.subtract(list.get(0),list.get(1));
               break;

        }

        return new ZcResult<Set>(set);

    }
}

ZpContext.java

/**
 * @description: 脚本解析上下文
 */
public class ZpContext {

    protected ZpContext(){}

    private Map<String, ZcResult> context = new ConcurrentHashMap<String, ZcResult>(256);


    /**
     *  是否存在变量名
     * @param var 变量名
     * @return
     */
    public boolean existVariable(String var){
       return context.containsKey(var);
    }

    /**
     * 放置变量
     * @param var 变量名
     * @param data 变量值
     */
    public void putVariable(String var,Object data){
        context.put(var,new ZcResult(data));
    }

    /**
     * 获取变量值
     * @param var 变量名
     * @return <T> 返回的结果
     */
    public <T> T getVariable(String var){
       ZcResult result = context.get(var);
       //不存在的变量会抛错
       if (null == result){
          throw new IllegalArgumentException("不存在变量:"+var);
       }
       return (T) result.getData();
    }

}

PrintOperator.java

/**
 * @description: 打印函数
 */
public class PrintOperator implements ZcOperator{
    /**
     * 打印方式标记
     */
    private int flag;
    /**
     * 打印目标
     */
    private Object target;

    public PrintOperator(int flag, Object target) {
        this.flag = flag;
        this.target = target;
    }

    public ZcResult operator() {
        Object t = target ;
        if(target instanceof ZcOperator){
            ZcOperator zo = (ZcOperator) target;
            t =zo.operator().getData();
        }

        switch (flag){
            case 1:
                System.out.print(t);
            case 2:
                System.out.println(t);
        }
        return ZcVoid.getInstance();
    }
}

测试用例

public class ZcodeTest {

    @Test
    void test1() throws ParseException {
        String prog =" a = 1+5*(7-3) ; \n"+
                " println(a); \n"+
                " u1 = {1,4,7+9}; \n"+
                " u2 = {'red','yellow'}; \n"+
                " u3 ={10+6,'red','ind'}; \n"+
                " unionSet = union(u1,u2,u3); \n"+
                " println(unionSet); \n"+
                " interSet = intersect(union(u1,u2),u3); \n"+
                " println(interSet); \n"+
                " println(subtract(u3,intersect(union(u1,u2),u3)));";

        Zcode zcode = new Zcode(prog);
        zcode.parse();
    }
}

执行结果

 从测试用例上可以看出,脚本语法解析能力已经具备了编程语言的雏形。这也就是javacc、antlr这类语法解析器生成器独特魅力的地方。当然大家也可以在他们的官网上找到一些以javacc、antlr开发的编程语言的语法解析样例供大家学习。

 上一篇:JAVACC使用总结(四):LOOKAHEAD解决语法选择冲突的利刃_IT不码农的博客-CSDN博客

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值