DIY一个简单的公式解析器

##模拟解析

($wget1#+0.7*0.3/0.1-1)*avg(1,3,4,5,$aa#-1)*($wget2#+0.9)-1

在这里插入图片描述

/**
 * 数学公式计算解析,支持函数内嵌套函数或公式
 * <br/>
 *  ($wget_1_1#+0.7*0.3/0.1-1)*avg(1,3,4,5,$aa#)*($wget_1_2#+0.9)-100" ;
 *  <br/>
 *  ($wget_1_1#+0.7)*($wget_1_2#+0.9)-100+50-($wget_1_3#+0.7)*($wget_1_4#+0.9) ;
 *
 *   <br/>
 *   ($wget_1_1#+0.7)*($wget_1_2#+0.9)-100+50-($wget_1_3#+0.7)*($wget_1_4#+0.9)
 *    <br/>
 *   ($x#-$y#)*3 ;
 *   <br/>
 *   1*3*4+1-2*3+123 ;
 * @author 346660561@qq.com
 * @date 2019-05-23 17:34
 * @description
 */
public class FormulaTreeParser {

  String expression;

  /**注册自定义函数*/
  List<String> functionRegister = FunctionEnum.getDiyFunc();

  Stack<Expression> stack = new Stack<>();

  int index;

  Object var ;

  static List<Character>  numbers = Arrays.asList('0','1','2','3','4','5','6','7','8','9');

  static List<Character>  judgingNegativeNumbers = Arrays.asList('(',  '(',  '+',  '-',  '*',  '/',  '%', ',' ,'"','\'');
  public FormulaTreeParser(String expression ){
    this(expression,0);
  }

  public FormulaTreeParser(String expression,int index,String ... fn){
    this.expression=  expression.trim();//.replace(" ","");
    this.index =index;
    if (fn!=null){
      functionRegister.addAll(Arrays.asList(fn));
    }
    functionRegister.stream().filter(t->!StringUtils.isEmpty(t)).distinct().collect(Collectors.toList());
  }
  public Expression parse(){
    return parse(null);
  }
  public Expression parse(Expression parent){
    if (Objects.nonNull(parent)){
      stack.push(parent);
    }
    FormulaTreeParser newParse;
    Expression expVar;
    while (true){
      Part part = next();
      switch (part){
        case ERROR:
          ResultCodeEnum resultCodeEnum = ResultCodeEnum.DEFINE_FORM_EXPRE_GRAMMA_INVALID ;
          throw new BusinessException(resultCodeEnum.message,resultCodeEnum.code);
        case OPEN:
          newParse =  new FormulaTreeParser(expression,index);
          Expression includeExpre = newParse.parse(null);
          var = includeExpre;
          this.index=newParse.index;
          break;
        case CLOSE:
          if (stack.isEmpty()){
            resultCodeEnum = ResultCodeEnum.DEFINE_FORM_EXPRE_GRAMMA_INVALID ;
            throw new BusinessException(resultCodeEnum.message,resultCodeEnum.code);
          }
          if (var instanceof Expression){
            stack.peek().add((Expression) var);
          }else{
            expVar = new Expression();
            expVar.setExpreVar(var);
            stack.peek().add(expVar);
          }
          return stack.firstElement();
        case VAR:
          break;
        case FUNCTION:
          newParse =  new FormulaTreeParser(expression,index+1);
          expVar = newParse.parse(stack.pop());
          var = expVar;
          this.index=newParse.index;
          break;
        case MD:
          findMulDiv(stack.pop());
          break;
        case MP:
          findMinPlu(stack.pop());
          break;
        case SPLIT:
          if (var instanceof Expression){
            expVar = (Expression) var;
          }else{
            expVar = new Expression();
            expVar.setExpreVar(var);
          }
          if (stack.size()>0){
            stack.peek().add(expVar);
          }
          //  内嵌函数是 如果stack.size>1表示内嵌 需要弹出
          if (stack.size()>1){
            stack.pop();
          }
          break;
        case END:
          expVar = new Expression();
          if (var instanceof Expression){
            if (stack.size()>0){
              stack.peek().add((Expression)var);;
            }else{
              stack.add((Expression) var);
            }
          }else{
            expVar.setExpreVar(var);
            stack.peek().add(expVar);
          }
          return stack.isEmpty()?null:stack.firstElement();
      }
    }
  }

  Part strConst(char quotationMark){
    int nextQuotationMark=expression.indexOf(quotationMark,index);
    var = expression.substring(index,nextQuotationMark);
    index+=((String) var).length()+1;
    return Part.VAR;
  }

  private Part next(){
    if (index>=expression.length()){
      return Part.END;
    }
    int indexThisPart =index;
    char nextPart  =expression.charAt(index);
    index++;
    if (nextPart==' '|| nextPart=='\t'||nextPart=='\n'){
      return next();
    }else if ('(' == nextPart||'(' == nextPart){
      return Part.OPEN;
    }else if(')' == nextPart||')' == nextPart){
      return Part.CLOSE;
    }else if ('$' ==nextPart ){
      int indexVar = expression.indexOf('#',index);
      var = '$'+expression.substring(index,indexVar)+'#';
      index+=((String)var).length()-1;
      return Part.VAR;
    }else if (numbers.contains(nextPart)){
      String numberStr = nextPart+"";
      for (int i =index;i<expression.length();i++){
        nextPart = expression.charAt(index);
        if ((numbers.contains(nextPart))||(nextPart=='.'&& !numberStr.contains("."))){
          numberStr+=nextPart;
          index++;
        }else {
          break;
        }
      }
      var = numberStr.contains(".")?Double.parseDouble(numberStr):Long.parseLong(numberStr);
      return Part.VAR;
    }else if ('\''==nextPart){
      return strConst('\'');
    }else if ('"' == nextPart  ){
      return strConst('"');
    }else if (nextPart=='+'  ){
      Expression expr = new Expression();
      expr.setFunc(FunctionEnum.PLUS.method);
      stack.push(expr);
      return Part.MP;
    } else if ( nextPart=='-'  ){
      String fn = FunctionEnum.get(nextPart).method;
      Expression expr = new Expression();
      if (indexThisPart== 0
        || judgingNegativeNumbers.contains(expression.charAt(indexThisPart-1))){
        next();
        expr.setExpreVar(var);
        Expression negative = new Expression();
        negative.add(expr);
        negative.setFunc(FunctionEnum.NEGATIVE.method);
        var = negative;
        return Part.VAR;
      }else{
        expr.setFunc(fn);
        stack.push(expr);
      }
      return Part.MP;
    } else if ( nextPart=='/' ||nextPart=='*' ||nextPart=='%'){
      String fn = FunctionEnum.get(nextPart).method;
      Expression expr = new Expression();
      expr.setFunc(fn);
      stack.push(expr);
      return Part.MD;
    }else if(nextPart ==','){
      return Part.SPLIT;
    }else{
      for (String func:functionRegister){
        if (expression.indexOf(func,index-1)== index-1){
          index+=func.length()-1;
          Expression expr = new Expression();
          expr.setFunc(func);
          stack.push(expr);
          return Part.FUNCTION;
        }
      }
      ResultCodeEnum resultCodeEnum = ResultCodeEnum.DEFINE_FORM_EXPRE_GRAMMA_INVALID ;
      throw new BusinessException(resultCodeEnum.message,resultCodeEnum.code);
    }
  }

  private  void findMinPlu(Expression exp){
    Expression expVar  ;
    if (Objects.isNull(var)|| StringUtils.isEmpty(var.toString())){
      ResultCodeEnum resultCodeEnum = ResultCodeEnum.DEFINE_FORM_EXPRE_GRAMMA_INVALID ;
      throw new BusinessException(resultCodeEnum.message,resultCodeEnum.code);
    }
    if (var instanceof Expression){
      expVar = (Expression) var;
      var= null;
    }else{
      expVar = new Expression();
      expVar.setExpreVar(var);
    }

    if (stack.isEmpty()){
      stack.push(exp);
      stack.peek(). add(expVar);
      return ;
    }
    if (functionRegister.contains(stack.peek().getFunc())){
      // 处理自定义函数内嵌套函数
      exp.add(expVar);
      stack.peek().add(exp);
    }else {
      stack.peek().add(expVar);
      Expression topFunc = stack.firstElement();
      if (functionRegister.contains(stack.firstElement().getFunc())){
        exp. add(stack.peek());
        topFunc.add(exp);
        topFunc.remove(stack.peek());
        stack.clear();
        stack.push(topFunc);
      }else{
        stack.clear();
        exp. add(topFunc);
      }
    }
    stack.push(exp);
  }

  /**
   *  exp=stack.pop()
   * @param exp
   */
  private void findMulDiv(Expression exp){
    if (Objects.isNull(var)|| StringUtils.isEmpty(var.toString())){
      ResultCodeEnum resultCodeEnum = ResultCodeEnum.DEFINE_FORM_EXPRE_GRAMMA_INVALID ;
      throw new BusinessException(resultCodeEnum.message,resultCodeEnum.code);
    }
    Expression expVar = new Expression();
    if (var instanceof Expression){
      expVar = (Expression) var;
      var= null;
    }else{
      expVar.setExpreVar(var);
    }
    if(stack.isEmpty()){
      stack.push(exp);
      stack.peek().add(expVar);
      return ;
    }
    Expression expPop = stack.pop();
    if(Objects.equals(expPop.getFunc(),FunctionEnum.DIVIDE.method)
        ||Objects.equals(expPop.getFunc(),FunctionEnum.MULTIPLY.method)){
      exp.add(expPop);
      expPop.add(expVar);
      if(!stack.isEmpty()){
        stack.peek().remove(expPop);
        stack.peek().add(exp);
      }
      stack.push(exp);
    } else if(Objects.equals(expPop.getFunc(),FunctionEnum.PLUS.method)
        ||Objects.equals(expPop.getFunc(), MINUS.method)
        ||functionRegister.contains(expPop.getFunc())
    ){
      expPop. add(exp);
      exp.add(expVar);
      stack.push(expPop);
      stack.push(exp);
    }
  }
  enum Part{
    OPEN,
    MP ,//加减
    MD,//乘、除、求余
    VAR,
    FUNCTION,
    CLOSE,
    SPLIT,
    END,
    ERROR
  }
}

/**公式解析出来变成计算树形结构
 * @author 346660561@qq.com
 * @date 2019-05-21 17:40
 * @description
 */
@Getter
@Setter
public class Expression   {

    @ApiModelProperty("变量或引用字段")
    @JsonProperty("expre_var")
    private Object expreVar;

    private List<Expression> items;

    /**
     * +   plus、
     * -   minus、
     * *  multiply、
     * /   divide、
     * %  residue、
     * >    greater_than、
     * >=  greater_equal
     * <    less_than
     * <=  less_equal
     * !=   no_equal
     * =   equal
     * |   or
     * &  and
     * ifelse
     */
    @ApiModelProperty(value="计算函数")
    String func;

    public Expression add(Expression param){
        if (items==null){
            items = new ArrayList<>();
        }
        items.add(param);
        return  this;
    }

    public Expression remove(Expression param){
        if (items==null||items.isEmpty()){
            return this;
        }
        items.remove(param);
        return  this;
    }
}
/** 
 * 常量定义 
 * @author 346660561@qq.com
 * @date 2019-05-21 17:40
 * @description
 */
public enum FunctionEnum {

   PLUS("plus","+",new NormalMathValidator(),new Plus())
  ,MINUS("minus","-",new NormalMathValidator(),new Minus())
  ,MULTIPLY("multiply","*",new NormalMathValidator(),new Multiply())
  ,DIVIDE("divide","/",new NormalMathValidator(),new Divide())
  ,RESIDUE("residue","%",new NormalMathValidator(),new Residue())
  ,AVG("avg","avg",new NormalMathValidator(),new Avg())
  ,SUM("sum","sum",new NormalMathValidator(),new Sum())
  ,COUNT("count","count",new NormalMathValidator(),new Count())
  ,MAX("max","max",new NormalMathValidator(),new Max())
  ,MIN("min","min",new NormalMathValidator(),new Min())

  ,IF_ELSE("ie","if",new IfElseValidator(),null)

  ,OR("or","||",null,null)
  ,AND("and","&&",null,null)
  ,GREATER_THAN("gt",">",null,null)
  ,GREATER_EQUAL("ge",">=",null,null)
  ,EQUAL("eq","=",null,null)
  ,NOT_EQUAL("ne","!=",null,null)
  ,LESS_THAN("lt","<",null,null)
  ,LESS_EQUAL("le","<=",null,null)

  ,NEGATIVE("negative","negative",null,new Negative())
  ,TODAY("today","today",null,new Today())
  ,NOW("now","now",null,new Now())

  ,ADD_DAY("addday","addday",null,new AddDay())
  ,DATE_DIFF("datediff","datediff",null,new DateDiff())
  ,WEEK_NUM("weeknum","weeknum",null,new WeekNum())
  ,DATE_FORMAT("format","format",null,new Format())

  ;

  public final String method ;
  public final String sign;
  public final FunctionValidator validator;
  public final MathCalculate calculator;

  FunctionEnum(String method,String sign,  FunctionValidator validator,MathCalculate calculator) {
    this.method = method;
    this.sign = sign;
    this.validator=validator;
    this.calculator = calculator;
  }

  public static FunctionEnum get(String method){
    if (StringUtils.isEmpty(method)){
      return null;
    }
    for(FunctionEnum mt:FunctionEnum.values()){
      if (Objects.equals(mt.method,method)){
        return mt;
      }
    }
    return null;
  }

  public static FunctionEnum get(char method){
    for (FunctionEnum fn:FunctionEnum.values()){
      if (Objects.equals(fn.sign.charAt(0),method)){
        return fn;
      }
    }
    return null;
  }

  public static List<String> p_m_m_d_r = Arrays.asList(PLUS.method,MINUS.method,MULTIPLY.method,DIVIDE.method,RESIDUE.method);

  public static List<String> getDiyFunc(){
    return Arrays.stream(FunctionEnum.values())
        .filter(tt->Objects.nonNull(tt.calculator))
        .filter(tt->!p_m_m_d_r.contains(tt.method))
        .map(tt->tt.method)
        .collect(Collectors.toList());
  }

  public FunctionValidator getValidator() {
    return validator;
  }

  public static List<FunctionEnum> aggregationFunc = Arrays.asList(AVG,SUM,COUNT,MAX,MIN);

  public static List<FunctionEnum> arithmetic = Arrays.asList(
      PLUS,MINUS,MULTIPLY,DIVIDE,RESIDUE,ADD_DAY,DATE_DIFF,WEEK_NUM,DATE_FORMAT
  );

}
···

 [GITHUB代码](https://github.com/zhuzho/FormulaTreeParser)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MICROSOFT 基础类库 : ExpressionProcess 项目概况 应用程序向导已为您创建了此 ExpressionProcess DLL。此 DLL 不仅 说明了使用 Microsoft 基础类的基础,而且 也是编写 DLL 的起点。 此文件包含组成 ExpressionProcess DLL 的每个文件的内容摘要。 ExpressionProcess.vcproj 这是用应用程序向导生成的 VC++ 项目的主项目文件。 它包含有关生成此文件的 Visual C++ 版本的信息,以及 有关使用应用程序向导选择的 平台、配置和项目功能的信息。 ExpressionProcess.cpp 这是包含 DllMain() 定义的主 DLL 源文件。 ExpressionProcess.rc 这是程序使用的所有 Microsoft Windows 资源的列表。 它包含存储在 RES 子目录下的图标、位图和光标。 此文件可在 Microsoft Visual C++ 中直接编辑。 resExpressionProcess.rc2 此文件包含不由 Microsoft Visual C++ 编辑的资源。您应将不能由 资源编辑器编辑的所有资源放置在此文件中。 ExpressionProcess.def 此文件包含有关运行 Microsoft Windows 所需的 DLL 的信息。 它定义此 DLL 的名称和说明等参数。 它还从此 DLL 导出函数。 其他标准文件: StdAfx.h、StdAfx.cpp 这些文件用于生成名为 ExpressionProcess.pch 的预编译头 (PCH) 文件以及名为 StdAfx.obj 的预编译类型文件。 Resource.h 这是标准的头文件,它定义了新的资源 ID。 Microsoft Visual C++ 读取和更新此文件。 其他注释: 应用程序向导使用 "TODO:" 指示 应增加或自定义的源代码部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值