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 |