精華區beta PLT 關於我們 聯絡資訊
unit ExprParser; interface uses SysUtils; type TFuncCallHandler = function(Name: string; const Values: array of Double): Double; EParseError = class(EAbort); { 使用範例: EvaluateExpr('x+y*z', ['x','y','z'], [1,2,3]); 代表令x=1,y=2,z=3,算出x+y*z的值 } function EvaluateExpr(Expr: string; const Variables: array of string; const VarValues: array of Double; FuncCallHandler: TFuncCallHandler = nil) : Single; function EvaluateToStr(Text: string; const Variables: array of string; const VarValues: array of Double): string; procedure TestParser; implementation uses Classes, Math; procedure Error(Msg: string); begin raise EParseError.Create(Msg); end; function EvaluateExpr(Expr: string; const Variables: array of string; const VarValues: array of Double; FuncCallHandler: TFuncCallHandler = nil) : Single; var P: Integer; PeekedToken: string; IsTokenPeeked: Boolean; function InternalGetToken: string; begin while Expr[P] in [#1..#32] do Inc(P); if Expr[P] = #0 then begin Result := #0; Exit; end; case Expr[P] of '+', '-', '*', '/', '^', '(', ')', ',', '=': begin Result := Expr[P]; Inc(P); Exit; end; '>': begin if Expr[P+1] = '=' then begin Inc(P, 2); Result := '>='; Exit; end; Result := Expr[P]; Inc(P); end; '<': begin if Expr[P+1] = '>' then begin Inc(P, 2); Result := '<>'; Exit; end; if Expr[P+1] = '=' then begin Inc(P, 2); Result := '<='; Exit; end; Result := Expr[P]; Inc(P); end; '!': begin if Expr[P+1] = '=' then begin Inc(P, 2); Result := '!='; Exit; end; Result := Expr[P]; Inc(P); end; end; if Expr[P] in ['a'..'z','A'..'Z','_'] then begin Result := ''; while Expr[P] in ['a'..'z','A'..'Z','_', '0'..'9'] do begin Result := Result + Expr[P]; Inc(P); end; Result := LowerCase(Result); Exit; end; if Expr[P] in ['0'..'9'] then begin Result := ''; while Expr[P] in ['0'..'9'] do begin Result := Result + Expr[P]; Inc(P); end; if Expr[P] = '.' then begin Result := Result + Expr[P]; Inc(P); while Expr[P] in ['0'..'9'] do begin Result := Result + Expr[P]; Inc(P); end; end; if Expr[P] in ['E', 'e'] then begin Result := Result + Expr[P]; Inc(P); if Expr[P] in ['+', '-'] then begin Result := Result + Expr[P]; Inc(P); end; while Expr[P] in ['0'..'9'] do begin Result := Result + Expr[P]; Inc(P); end; end; Exit; end; Error('unrecognized char for token: ' + Expr[P]); end; function PeekToken: string; begin if IsTokenPeeked then Result := PeekedToken else begin Result := InternalGetToken; PeekedToken := Result; IsTokenPeeked := True; end; end; function GetToken: string; begin if IsTokenPeeked then begin Result := PeekedToken; IsTokenPeeked := False; end else Result := InternalGetToken; end; function EvalFactor: Single; forward; function EvalPower: Single; begin Result := EvalFactor; if PeekToken = '^' then begin GetToken; Result := Power(Result, EvalPower); end; end; function EvalMulDiv: Single; begin Result := EvalPower; while True do begin if PeekToken = '*' then begin GetToken; Result := Result * EvalPower; end else if PeekToken = '/' then begin GetToken; Result := Result / EvalPower; end else Break; end; end; function EvalAddSub: Single; begin if PeekToken = '-' then begin GetToken; Result := - EvalMulDiv; end else Result := EvalMulDiv; while True do begin if PeekToken = '+' then begin GetToken; Result := Result + EvalMulDiv; end else if PeekToken = '-' then begin GetToken; Result := Result - EvalMulDiv; end else Break; end; end; function EvalLogicRel: Single; begin Result := EvalAddSub; if PeekToken = '>' then begin GetToken; Result := Ord(Result > EvalLogicRel); end else if PeekToken = '<' then begin GetToken; Result := Ord(Result < EvalLogicRel); end else if PeekToken = '=' then begin GetToken; Result := Ord(Result = EvalLogicRel); end else if (PeekToken = '<>') or (PeekToken = '!=') then begin GetToken; Result := Ord(Result <> EvalLogicRel); end end; function EvalLogicAndOr: Single; begin Result := EvalLogicRel; while True do begin if PeekToken = 'and' then begin GetToken; Result := Ord((Result <> 0) and (EvalLogicRel <> 0)); end else if PeekToken = 'or' then begin GetToken; Result := Ord((Result <> 0) or (EvalLogicRel <> 0)); end else Break; end; end; function EvalFuncCall(Name: string; Params: TList): Single; var Count: Integer; List: array of Double; i: Integer; function R(X: Integer): Single; begin Result := Single(Params[i]); end; function GetMax: Double; var i: Integer; begin Result := List[0]; for i:=1 to Count-1 do if List[1] > Result then Result := List[1]; end; function GetMin: Double; var i: Integer; begin Result := List[0]; for i:=1 to Count-1 do if List[1] < Result then Result := List[1]; end; function GetBetween: Double; begin Result := Ord((List[0] >= List[1]) and (List[0] <= List[2]) or (List[0] >= List[2]) and (List[0] <= List[1])); end; function GetIf: Double; begin if List[0] <> 0 then Result := List[1] else Result := List[2]; end; begin Result := 0; Name := LowerCase(Name); Count := Params.Count; SetLength(List, Count); for i:=0 to Params.Count-1 do List[i] := Single(Params[i]); if (Name = 'sin') and (Count = 1) then Result := Sin(List[0]) else if (Name = 'cos') and (Count = 1) then Result := Cos(List[0]) else if (Name = 'tan') and (Count = 1) then Result := Tan(List[0]) else if (Name = 'exp') and (Count = 1) then Result := Exp(List[0]) else if (Name = 'log10') and (Count = 1) then Result := Log10(List[0]) else if (Name = 'abs') and (Count = 1) then Result := Abs(List[0]) else if (Name = 'stddev') and (Count >= 1) then Result := StdDev(List) else if (Name = 'variance') and (Count >= 1) then Result := Variance(List) else if ((Name = 'average') or (Name = 'mean') or (Name = 'avg')) and (Count >= 1) then Result := Mean(List) else if (Name = 'max') and (Count >= 1) then Result := GetMax else if (Name = 'min') and (Count >= 1) then Result := GetMin else if (Name = 'between') and (Count = 3) then Result := GetBetween else if (Name = 'if') and (Count = 3) then Result := GetIf else begin if Assigned(FuncCallHandler) then try Result := FuncCallHandler(Name, List); except on EAbort do Error('function not defined: ' + Name); else raise; end else Error('function not defined: ' + Name); end; end; function EvalVariable(Name: string): Double; var i: Integer; begin for i:=0 to High(Variables) do if AnsiCompareText(Variables[i], Name) = 0 then begin Result := VarValues[i]; Exit; end; Result := 0; Error('variable not defined: ' + Name); end; function EvalExpr: Single; begin Result := EvalLogicAndOr; end; function EvalFactor: Single; var V: string; Values: TList; begin case PeekToken[1] of '(': begin GetToken; Result := EvalExpr; if GetToken <> ')' then Error('unmatched right parenthese ")"'); Exit; end; 'A'..'Z','a'..'z','_': begin V := GetToken; if PeekToken = '(' then // function must be processed begin GetToken; Values := TList.Create; try if PeekToken = ')' then // Empty function is an exceptional parse GetToken else begin while True do begin Values.Add(Pointer(EvalExpr)); case GetToken[1] of ')': Break; ',': ; else Error('function call missing "," or ")"'); end; end; end; Result := EvalFuncCall(V, Values); finally Values.Free; end; end else Result := EvalVariable(V); Exit; end; '0'..'9': begin Result := StrToFloat(GetToken); Exit; end; end; Result := 0; Error('unexpected token during getting factor: ' + PeekToken); end; begin if Expr = '' then Error('empty expression'); P := 1; IsTokenPeeked := False; Result := 0; try Result := EvalExpr; except on EConvertError do Error('conversion error'); on EZeroDivide do Error('divide by zero error'); else raise; end; end; procedure TestParser; var S: string; begin while True do begin ReadLn(S); if S = '' then Halt; try WriteLn(EvaluateExpr(S, ['x','y','z'], [1,2,3])); except on E: EParseError do WriteLn('Error: ' + E.Message); else raise; end; end; end; function EvaluateToStr(Text: string; const Variables: array of string; const VarValues: array of Double): string; begin Result := ''; if Text = '' then Exit; try Result := FloatToStr(EvaluateExpr(Text, Variables, VarValues)); except on EConvertError do Result := '數值格式錯誤'; on E: EParseError do Result := '編譯錯誤:' + E.Message; else raise; end; end; end. -- Chaos is the best description of the constant state of human society. Therefore, dynamic balance is required for us to to survive when vital fault occurrs. So in the society, we don't chase for peace and order, but existence and survival, instead. -- ※ 發信站: 批踢踢實業坊(ptt.twbbs.org) ◆ From: 140.112.3.12