View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0006044 | JEDI Code Library | JclExprEval | public | 2012-12-03 20:37 | 2024-01-02 21:24 |
Reporter | davidheffernan | Assigned To | |||
Priority | normal | Severity | feature | Reproducibility | N/A |
Status | resolved | Resolution | fixed | ||
Product Version | Version 2.5 (Subversion repository/Daily zips) | ||||
Target Version | Fixed in Version | ||||
Summary | 0006044: Add a power operator | ||||
Description | I'd like to see a power operator added to the expression evaluator. My personal choice would be to use ^. There would need to be an extra precedence level introduced. So the operator precedence list would look like this: // (highest) not bnot(bitwise) +(unary) -(unary) (level 4) // ^ (level 3) // * / div mod and band(bitwise) shl shr (level 2) // +(binary) -(binary) or xor bor(bitwise) bxor(bitwise) (level 1) // (lowest) < <= > >= cmp = <> (level 0) | ||||
Additional Information | Implementing this is pretty simple. The two classes that need work are TExprCompileParser and TExprEvalParser. Let's look at the latter. The function currently named EvalExprLevel4 would become. And in the implementation it would call EvalExprLevel4 instead of EvalExprLevel3. And a new function named EvalExprLevel3 would have to be added to implement the power operator. These routines would look like this: function TExprEvalParser.EvalExprLevel3(ASkip: Boolean): TFloat; begin Result := EvalExprLevel4(ASkip); while True do case Lexer.CurrTok of etArrow: Result := Power(Result, EvalExprLevel4(True)); else Break; end; end; function TExprEvalParser.EvalExprLevel4(ASkip: Boolean): TFloat; begin if ASkip then Lexer.NextTok; case Lexer.CurrTok of etPlus: Result := EvalExprLevel4(True); etMinus: Result := -EvalExprLevel4(True); etIdentifier: // not, bnot if AnsiSameText(Lexer.TokenAsString, 'not') then begin if EvalExprLevel4(True) <> 0.0 then Result := 0.0 else Result := 1.0; end else if AnsiSameText(Lexer.TokenAsString, 'bnot') then Result := not Round(EvalExprLevel4(True)) else Result := EvalFactor; else Result := EvalFactor; end; end; | ||||
Tags | No tags attached. | ||||
Fixed in GIT commit | 74e0119b6ff40952dd670d91d2487e502ad01fbe | ||||
Fixed in SVN revision | |||||
IDE version | All | ||||
|
thanks, I needed to add a power operator. This option should be the default.. |
|
Could you create a patch file and a small sample. Then I will try to commit it. I'm not familiar with this objects. |
2013-06-27 20:27
|
JclExprEvalPowerOperator.patch (6,622 bytes)
diff --git "a/C:\\Users\\heff\\AppData\\Local\\Temp\\TortoiseGit\\JclF486.tmp\\JclExprEval-2f4215d-left.pas" "b/C:\\Users\\heff\\Desktop\\jcl\\jcl\\source\\common\\JclExprEval.pas" index 9f62a66..bf357e4 100644 --- "a/C:\\Users\\heff\\AppData\\Local\\Temp\\TortoiseGit\\JclF486.tmp\\JclExprEval-2f4215d-left.pas" +++ "b/C:\\Users\\heff\\Desktop\\jcl\\jcl\\source\\common\\JclExprEval.pas" @@ -40,7 +40,8 @@ // all binary operators are associated from left to right // all unary operators are associated from right to left -// (highest) not bnot(bitwise) +(unary) -(unary) (level 3) +// (highest) not bnot(bitwise) +(unary) -(unary) (level 4) +// ^ (level 3) // * / div mod and band(bitwise) shl shr (level 2) // +(binary) -(binary) or xor bor(bitwise) bxor(bitwise) (level 1) // (lowest) < <= > >= cmp = <> (level 0) @@ -61,9 +62,9 @@ uses JclUnitVersioning, {$ENDIF UNITVERSIONING} {$IFDEF HAS_UNITSCOPE} - System.SysUtils, System.Classes, + System.SysUtils, System.Classes, System.Math, {$ELSE ~HAS_UNITSCOPE} - SysUtils, Classes, + SysUtils, Classes, Math, {$ENDIF ~HAS_UNITSCOPE} JclBase, JclSysUtils, JclStrHashMap, JclResources; @@ -441,6 +442,7 @@ type function Subtract(ALeft, ARight: TExprNode): TExprNode; virtual; abstract; function Multiply(ALeft, ARight: TExprNode): TExprNode; virtual; abstract; function Divide(ALeft, ARight: TExprNode): TExprNode; virtual; abstract; + function Power(ALeft, ARight: TExprNode): TExprNode; virtual; abstract; function IntegerDivide(ALeft, ARight: TExprNode): TExprNode; virtual; abstract; function Modulo(ALeft, ARight: TExprNode): TExprNode; virtual; abstract; function Negate(AValue: TExprNode): TExprNode; virtual; abstract; @@ -486,6 +488,7 @@ type function CompileExprLevel1(ASkip: Boolean): TExprNode; virtual; function CompileExprLevel2(ASkip: Boolean): TExprNode; virtual; function CompileExprLevel3(ASkip: Boolean): TExprNode; virtual; + function CompileExprLevel4(ASkip: Boolean): TExprNode; virtual; function CompileFactor: TExprNode; virtual; function CompileIdentFactor: TExprNode; virtual; public @@ -505,6 +508,7 @@ type function EvalExprLevel1(ASkip: Boolean): TFloat; virtual; function EvalExprLevel2(ASkip: Boolean): TFloat; virtual; function EvalExprLevel3(ASkip: Boolean): TFloat; virtual; + function EvalExprLevel4(ASkip: Boolean): TFloat; virtual; function EvalFactor: TFloat; virtual; function EvalIdentFactor: TFloat; virtual; public @@ -607,6 +611,7 @@ type function Subtract(ALeft, ARight: TExprNode): TExprNode; override; function Multiply(ALeft, ARight: TExprNode): TExprNode; override; function Divide(ALeft, ARight: TExprNode): TExprNode; override; + function Power(ALeft, ARight: TExprNode): TExprNode; override; function IntegerDivide(ALeft, ARight: TExprNode): TExprNode; override; function Modulo(ALeft, ARight: TExprNode): TExprNode; override; function Negate(AValue: TExprNode): TExprNode; override; @@ -1216,20 +1221,33 @@ end; function TExprCompileParser.CompileExprLevel3(ASkip: Boolean): TExprNode; begin + Result := CompileExprLevel4(ASkip); + + while True do + case Lexer.CurrTok of + etArrow: + Result := NodeFactory.Power(Result, CompileExprLevel4(True)); + else + Break; + end; +end; + +function TExprCompileParser.CompileExprLevel4(ASkip: Boolean): TExprNode; +begin if ASkip then Lexer.NextTok; case Lexer.CurrTok of etPlus: - Result := CompileExprLevel3(True); + Result := CompileExprLevel4(True); etMinus: - Result := NodeFactory.Negate(CompileExprLevel3(True)); + Result := NodeFactory.Negate(CompileExprLevel4(True)); etIdentifier: // not, bnot if AnsiSameText(Lexer.TokenAsString, 'not') then - Result := NodeFactory.LogicalNot(CompileExprLevel3(True)) + Result := NodeFactory.LogicalNot(CompileExprLevel4(True)) else if AnsiSameText(Lexer.TokenAsString, 'bnot') then - Result := NodeFactory.BitwiseNot(CompileExprLevel3(True)) + Result := NodeFactory.BitwiseNot(CompileExprLevel4(True)) else Result := CompileFactor; else @@ -1451,25 +1469,38 @@ end; function TExprEvalParser.EvalExprLevel3(ASkip: Boolean): TFloat; begin + Result := EvalExprLevel4(ASkip); + + while True do + case Lexer.CurrTok of + etArrow: + Result := Power(Result, EvalExprLevel4(True)); + else + Break; + end; +end; + +function TExprEvalParser.EvalExprLevel4(ASkip: Boolean): TFloat; +begin if ASkip then Lexer.NextTok; case Lexer.CurrTok of etPlus: - Result := EvalExprLevel3(True); + Result := EvalExprLevel4(True); etMinus: - Result := -EvalExprLevel3(True); + Result := -EvalExprLevel4(True); etIdentifier: // not, bnot if AnsiSameText(Lexer.TokenAsString, 'not') then begin - if EvalExprLevel3(True) <> 0.0 then + if EvalExprLevel4(True) <> 0.0 then Result := 0.0 else Result := 1.0; end else if AnsiSameText(Lexer.TokenAsString, 'bnot') then - Result := not Round(EvalExprLevel3(True)) + Result := not Round(EvalExprLevel4(True)) else Result := EvalFactor; else @@ -1903,6 +1934,11 @@ type procedure Execute; override; end; + TExprPowerVmOp = class(TExprBinaryVmOp) + public + procedure Execute; override; + end; + TExprCompareVmOp = class(TExprBinaryVmOp) public procedure Execute; override; @@ -2236,6 +2272,13 @@ begin FOutput := FLeft^ / FRight^; end; +//=== { TExprPowerVmOp } ===================================================== + +procedure TExprPowerVmOp.Execute; +begin + FOutput := Power(FLeft^, FRight^); +end; + //=== { TExprCompareVmOp } =================================================== procedure TExprCompareVmOp.Execute; @@ -3127,6 +3170,11 @@ begin Result := AddNode(TExprBinaryVmNode.Create(TExprDivideVmOp, [ALeft, ARight])); end; +function TExprVirtMachNodeFactory.Power(ALeft, ARight: TExprNode): TExprNode; +begin + Result := AddNode(TExprBinaryVmNode.Create(TExprPowerVmOp, [ALeft, ARight])); +end; + function TExprVirtMachNodeFactory.IntegerDivide(ALeft, ARight: TExprNode): TExprNode; begin Result := AddNode(TExprBinaryVmNode.Create(TExprIntegerDivideVmOp, [ALeft, ARight])); |
|
I have just added a patch file. And here's a very simple demo sample that exercises the operator precedence rules. program Mantis0006044demo; {$APPTYPE CONSOLE} uses SysUtils, Math, JclExprEval; type TTestExpressionProc = reference to procedure(const Expression: string; const Expected: Double); procedure DoTest(const TestExpression: TTestExpressionProc); begin TestExpression('1.3^2.5*4.8', Power(1.3,2.5)*4.8); TestExpression('1.3^2.5*4.8', Power(1.3,2.5)*4.8); TestExpression('2.0*1.3^2.5+4.8', 2.0*Power(1.3,2.5)+4.8); TestExpression('2.0*1.3^-2.5+-4.8', 2.0*Power(1.3,-2.5)-4.8); end; procedure Main; var Evaluator: TEvaluator; CompiledEvaluator: TCompiledEvaluator; TestExpression: TTestExpressionProc; begin Evaluator := TEvaluator.Create; try TestExpression := procedure(const Expression: string; const Expected: Double) begin Writeln(Format('Expected=%.6f, Evaluator=%.6f', [Expected, Evaluator.Evaluate(Expression)])); end; Writeln('TEvaluator:'); DoTest(TestExpression); Writeln; finally Evaluator.Free; end; CompiledEvaluator := TCompiledEvaluator.Create; try TestExpression := procedure(const Expression: string; const Expected: Double) begin CompiledEvaluator.Compile(Expression); Writeln(Format('Expected=%.6f, Evaluator=%.6f', [Expected, CompiledEvaluator.Evaluate])); end; Writeln('TCompiledEvaluator:'); DoTest(TestExpression); finally Evaluator.Free; end; end; begin try Main; Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
Date Modified | Username | Field | Change |
---|---|---|---|
2012-12-03 20:37 | davidheffernan | New Issue | |
2012-12-03 20:37 | davidheffernan | IDE version | => All |
2013-03-17 11:39 | alquimista | Note Added: 0020451 | |
2013-06-24 21:59 | jfudickar | Note Added: 0020536 | |
2013-06-24 22:01 | jfudickar | Status | new => feedback |
2013-06-27 20:27 | davidheffernan | File Added: JclExprEvalPowerOperator.patch | |
2013-06-27 20:29 | davidheffernan | Note Added: 0020547 | |
2024-01-02 21:24 | AHUser | Status | feedback => resolved |
2024-01-02 21:24 | AHUser | Resolution | open => fixed |
2024-01-02 21:24 | AHUser | Fixed in GIT commit | => 74e0119b6ff40952dd670d91d2487e502ad01fbe |