小弟正在开发考勤薪资系统
打算做个公式编辑器
例如客户输入如下:工资=底薪*本月天数()+加班时数*1.5*底薪如何分析并计算该公式
底薪,加班小时是某表字段
本月天数()是提供给客户的函数,计算当月的天数。
不知有哪位大侠做过该模块,请指点一二。

解决方案 »

  1.   

    http://hubdog.csdn.net/Hubdog/Script.htm
      

  2.   

    s_sql = update table1 set gz = dx*ts + jbss * 1.5*dx/A;
    {A:正常上班时数}exec(s_sql);
      

  3.   

    To:
      pokemonFK() 
    本月天数() 是我提供给客户的函数,用来计算天数的
    但我不知道怎么来替换本月天数()为SQL语句是别的函数
    又如
    假如我提供给客户判断语句 ‘如果(条件) 那么() 否则()’
    我怎么把这句解析成SQL可以识别的语句。
      

  4.   

    以前搜集的
    数据结构(PASCAL语言实现)书上讲述了一个把所有符号和数字推堆栈的方法实现的表达式解析,给你一段网上找到的例子:
    一款设计精巧的表达式解析器---- 开发MIS系统时,报表设计中经常会碰到表达式解释器,完成用户自定义的公式运算。这种程序的设计需要有比较高的技巧,以下介绍一款用DELPHI4.0开发的程序[程序重在算法,语言特性很少,用其它语言的人也能读懂],只要按自已的要求稍加修改,即可做成组件或全局方法发部。它支持 "加[+]、减[-]、乘[*]、除[/]、商[$:两整数相除,结果的整数部分]、模[%]、括号[()]"四则混合运算,支持"与[&]、或[|]、异或[^]、左移[< ]、右移[ >]和非[!]"逻辑运算功能,同时它们可以出现在同一个表达式中,它们的优先级依次为括号、非、与或异或左右移、乘除商模、加减。如式:12.45+3*16 >2*(3+6*(3+2)-1)=12.45+3*4*32,计算结果为:396.45。程序包括两大部分功能:表达式拆解、因子计算,分别由两个类TBdsProc和TPUSHPOP完成。具体如下:CDIKind=record
      case id: Boolean of
      True: (dval: Double);
      False: (ival: Integer);
    end;CDKind:区别表达式中的整数和浮点数类型,因为有些运算符不支持浮点数(如逻辑运算)。  ValKind = CDIKind;
      TBdsProc = class
    private
      Fghpd : Integer;//识别并标记左右括号是否成对出现
      function IsCalcFh(c: Char): boolean;//判别一个字符是否运算符
      function CopyRight(abds: String;start: Integer):String;//截取字符串表达式
      function BdsSs(var abds: String): ValKind;//返回一个子表达式的值
      function BdsYz(var abds: String): ValKind;//表达式因子,如:15、(13+5)
      function BdsItm(var abds: String): ValKind;//读取表达式中的一个因子
    public
      function CalcValue(const bds: String): ValKind;//返回计算结果
    end;TPUSHPOP = class
    private
      ffh: array [0..2] of Char;//符号数组
      value: array [0..3] of CDIKind;//值数组
      flevel: Byte;//因子个数
      fisfh: Boolean;//识别等待输入值或运算符
      fisnot: Boolean;//识别待计算数据项是否执行非运算
      function Calcsj(av1,av2: CDIKind;fh: Char): CDIKind;  //执行两个数值的四则运算
      function Calclg(av1,av2: CDIKind; fh: Char): CDIKind;//执行两个数的逻辑运算
      procedure Calccur;{当输入数据项满足四个数后[依运算优先级层数求得,见下述算式解析原理]执行中间运算}
      function IsLgFh(fh: Char): Boolean;//一个符号是否逻辑运算符
      function IsCcFH(fh: Char): Boolean;// 一个符号乘除商模运算符
    public
      constructor Create;
      procedure PushValue(avalue: CDIKind);//存入一个数据项
      procedure PushFh(afh: Char);//存入一个符号
      function CalcValue: CDIKind;//计算并返回值
    end;
    ---- 表达式解析基本原理:
    ---- 1.表达式处理:
    ---- 表达式的一个个数据项组成,中间由运算符连接,每个数据项为一个分析基本分析单元。表达式中如果包含有改变运算优先级别的括号运算,先计出括号中式子的值,再把该值当一个数据项处理,这一点在程序设计中只要运用递归功就能实现。
    ---- 2.数据项计算处理
    ---- a >非运算:
    ---- 它为单目运算符,级别最高,在存入符号时做标记,存入数据时即时计算并去除标记。
    ---- b >表达式运算:
    ---- 设f1、f2、f3分别表示一二三级运算符,V1、V2、V3、V4分别表示顺序四个数,则极端表达式模型为R=V1 f1 V2 f2 V3 f3 V4 …,计算时顺序应为 R=…V4 f3 V3 f2 V2 f1 V1。为了简化运算,把其中运算级别最高的逻辑运算在存入数据时先计算完成, 初始化时设V1=0,第一个运算符设为'+'。则公式化为: R=…V4 f2(f1) V3 f2(f1) V2 f1 V1。这样,当V2与V3间的运算符级别为f2时,V4与V3间的运算符级别< =f2,则:V2 =(V2与V3计算值),V3后的值和运算符前移;若V2与V3间的运算级别为f1,可先算V1与V2,V2以后的值和运算符前移。则计算后的表达式为:R=V3 f2(f2) V2 f1 V1刚好满足循环取数条件。
    ---- 3.实现:
    ---- 程序比较长(TBdsProc和TPUSHPOP的源代码合计长度为400多行),完整代码见附件,以下对一些重要实现方法做介绍:
    ---- < 1 >表达式拆解:由方法BdsSs和BdsYz完成表达式拆解和因子处理
    function TBdsProc.BdsSs(var abds: String): ValKind;
    var
      c: Char;
      lpp: TPushPop;
    begin
      lpp := TPushPop.Create;//建立数据计算对象
      while abds< >'' do begin
        c := abds[1];
        if IsCalcFh(c) then begin//是否运算符
          lpp.PushFh(c);//保存运算符
          abds := CopyRight(abds,2);
        end else begin
          if c=')' then begin
            Dec(Fghpd);//括号匹配
            abds := CopyRight(abds,2);
            if Fghpd < 0 then Raise Exception.Create('括号不配对');
            Result := lpp.CalcValue;//返回括号中的子项值,进行下一步计算
            lpp.Free;
            Exit;
          end else begin
            if c='(' then Inc(Fghpd);//做括号层数标识
            lpp.PushValue(BdsYz(abds));//取下一项的值。
          end;
        end;
      end;
      if Fghpd< >0 then Raise Exception.Create('括号不配对');
      Result := lpp.CalcValue;//返回最终运算值
      lpp.Free;
    end;function TBdsProc.BdsYZ(var abds: String): ValKind;
    begin
      if abds< >'' then begin
        if abds[1]='(' then begin
          abds := CopyRight(abds,2);
          Result := BdsSs(abds);//递归调用,求括号中的值
        end else Result := BdsItm(abds);{读一个数据项,如果包括函数定义,可以在该方法中定义接口或连接}
      end;
    end;若表达式要支持函数功能,只要定义系统支持的函数,在取行表达式因子BdsItm中加入连接函数定义即可,如下:
    …..
    else if (c< ='Z') and (c >='A') then begin
      bhs := 取出函数名及参数
      Result := … //调用函数处理 (函数定义)
      abds := 下一项
    end
    ….< 2 > 数据计算:主要包PushValue,PushFh,Calccur分别完成存入数、符号、中间计算
    procedure TPUSHPOP.PushValue(avalue: CDIKind);
    begin
      if fisfh=True then Raise Exception.Create('缺少运算符');
      if fisnot then begin//进行非运算
        if avalue.id then Raise Exception.Create('浮点数不能做非运算');
        avalue.ival := not avalue.ival;
        fisnot := False;
      end;
      if IsLgFh(ffh[flevel]) then begin//运行逻辑运算
        value[flevel] := Calclg(value[flevel],
        avalue,ffh[flevel]);//与当前值做逻辑运算
      end else begin
        Inc(flevel);//存数位置指针加1
        value[flevel] := avalue;//存入值
        if flevel >2 then Calccur;//数据个数达到4,进行中间运算
      end;
      fisfh := True;//输入符号可见
    end;procedure TPUSHPOP.PushFh(afh: Char);
    begin
      if (fisfh=false) then begin//非运算是一级
        if (afh='!') and (not fisnot) then begin//标识非运算
          fisnot := True;
          Exit;
        end else Raise Exception.Create('运算符重复');
      End Else begin
        ffh[flevel] := afh;//存入运算符
        fisfh := False; 输入值可见
      end;
    end;procedure TPUSHPOP.Calccur;
    begin
      if IsCcFh(ffh[1]) then begin//二级运算符
        value[1] := Calcsj(value[1],value[2],ffh[1]);//计算2和3项的值
        ffh[1] := ffh[2];//后序运符和值前移
        value[2] := value[3];
      end else begin//一级运算符
        value[0] := Calcsj(value[0],value[1],ffh[0]);//计算1和2项的值
        value[1] := value[2];{ 后序运符和值前移,2和3项的值不计算是为了保持第一个运算符为一级运算符}
        value[2] := value[3];
        ffh[0] := ffh[1];
        ffh[1] := ffh[2];
      end;
      Dec(flevel);//存数位置指针减1
    end;
      

  5.   

    function Calcalute(Express: String): Double; 
    //Calcalute Expression Value
    //Created : 549@17:19 2004-02-28
    //Modified: 549@14:57 2004-06-19
      function MulDivAddSub(var Express: String): Double;
      //mul and div first, then add and sub
        function ProcessAtPos(var Express: String; Var Pos: Integer): Double;
        var
          Left: Integer;
          Right: Integer;
          Num1,Num2:Double;
        begin
          Right := Pos + 2;
          Left := Pos - 1;
          Result := 0;
          while ((Express[Left] >= '0') and (Express[Left] <= '9') or (Express[Left] = '.')) do
            if Left > 0 then dec(Left)
            else
              Break;
          while ((Express[Right] >= '0') and (Express[Right] <= '9') or (Express[Right] = '.')) do
            if Right <= Length(Express) then Inc(Right)
            else
              Break;
          if (Express[Right] <> '') then Dec(Right);
          if (Express[Left] = '-') and (Left = 1) then begin
            Dec(Left);
          end;
          try
            Num1 := StrToFloat(Copy(Express,Left+1,Pos-Left-1));
          except
            Num1 := 0;
          end;
          try
            Num2 := StrToFloat(Copy(Express,Pos + 1,Right-Pos));      except
            Num2 := 0;
          end;
          if Express[pos] = '*' then
            Result := Num1 * Num2
          else if Express[pos] = '/' then
            Result := Num1 / Num2
          else if Express[pos] = '+' then
            Result := Num1 + Num2
          else if Express[pos] = '-' then
            Result := Num1 - Num2;
          if Result <> 0 then
            Express := LeftStr(Express,Left) + VarToStr(Result) + RightStr(Express,Length(Express) - Right);
          if Express[1] = '(' then
            Express := RightStr(Express, Length(Express) -1);
          //relocate Pos
          if ((Left = 0) or (Left > Length(Express))) then
            Left := 1;
          Pos := Length(VarToStr(Result)) + Left - 1;
          //showmessage('Left:'+VarToStr(Left)+#13+'Right:'+VarToStr(Right)+Format('Exchange %f and %f',[Num1,Num2])+'Result : '+Express+#13+'Pos is:'+VarToStr(Pos)+#13+'Value at pos : '+Express[Pos]);    end;  var
        i: Integer;
        HaveAResult: Boolean;
      begin
        HaveAResult := False;
        //mul and div first , add and sub second
        while not HaveAResult do begin
          for i := 1 to Length(Express) do begin
            case Express[i] of
              '*','/': ProcessAtPos(Express,i);
            end;
          end;
          for i := 1 to Length(Express) do begin
            case Express[i] of
              #0     : Break;
              '+','-': ProcessAtPos(Express,i);
            end;
          end;
         //if exist '+', '-', '*', '/', process it
         for i := Length(Express) downto 1 do begin
            case Express[i] of
              '+','-','*','/': Break;
            end;
          end;
          HaveAResult := (i = 0) or ((i = 1) and ((Express[1] = '+') or (Express[1] = '-')));    end;
        Result := StrToFloat(Express);
      end;
      function CheckExpression(var Express: String): Boolean;
      //Check syntax
      var i: Integer;
          Left: Integer;
          TmpStr: String;
      begin
        Result := False;
        Left := 1;
        if Express = '' then Exit;
        //Delete blank
        TrimAll(Express);
        //Check illegal character and bracket matching
        for i := 1 to Length(Express) do begin
          case Express[i] of
            '0'..'9','+','-','*','/','.': Continue;
            '(': Inc(Left);
            ')': Dec(Left);
          else
            Exit;
          end;
          if Left < 1 then Exit;
        end;
        Result := Left = 1;
      end;
      function Calc(Express: String): Double;
      //Calculate expression
      var Right: Integer;
          Left: Integer;
          Para: String;
          Temp: Double;
      begin
        Result := 0;
        if Express = '' then Exit;
        //Begin Calculate
        while True do begin
          for Right := 1 to Length(Express) do begin
            case Express[Right] of
            ')',#0: Break;
            end;
          end;
          for Left := Right downto 1 do begin
            case Express[Left] of
            '(': Break;
            end;
          end;
          if Left <> 0 then begin
            //bracket first
            Para := Copy(Express,Left+1,Right-Left-1);
            Temp := MulDivAddSub(Para);
            Express := LeftStr(Express,Left-1) + VarToStr(Temp) + RightStr(Express,Length(Express) - Right);
          end
          else
          if Right = Length(Express) + 1 then begin
            //no bracket, mul and div first, then add and sub
            Result := MulDivAddSub(Express);
            Exit;
          end;    end;
      end;
    begin
      Result := 0;
      if not CheckExpression(Express) then Exit;
      Result := Calc(Express);
    end;
    //在使用前,先把你的变量替换成相应的数值
      

  6.   

    支持空格的版本在这里
    http://borland.mblogger.cn/lw549/posts/808.aspx