diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 4b373f7..b988ee1 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -4,7 +4,7 @@ 20.3 FMX True - Debug + Release Win64 ASTPlayground 2 diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index cb4d533..8dd397b 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -395,7 +395,6 @@ end; function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; var leftValue, rightValue: TDataValue; - leftScalar, rightScalar: TScalar; begin leftValue := Node.Left.Accept(Self); rightValue := Node.Right.Accept(Self); @@ -403,51 +402,30 @@ begin if (leftValue.Kind <> vkScalar) or (rightValue.Kind <> vkScalar) then raise ENotSupportedException.Create('Binary operations are only supported for scalar types.'); - leftScalar := leftValue.AsScalar; - rightScalar := rightValue.AsScalar; - - if not TScalar.IsBinaryOperatorSupported(Node.Operator, leftScalar.Kind, rightScalar.Kind) then - raise ENotSupportedException.Create('Binary operation not supported for scalar type.'); - - case Node.Operator of - boAdd: Result := leftScalar + rightScalar; - boSubtract: Result := leftScalar - rightScalar; - boMultiply: Result := leftScalar * rightScalar; - boDivide: Result := leftScalar / rightScalar; - boEqual: Result := TScalar.FromBoolean(leftScalar = rightScalar); - boNotEqual: Result := TScalar.FromBoolean(leftScalar <> rightScalar); - boLess: Result := TScalar.FromBoolean(leftScalar < rightScalar); - boGreater: Result := TScalar.FromBoolean(leftScalar > rightScalar); - boLessOrEqual: Result := TScalar.FromBoolean(leftScalar <= rightScalar); - boGreaterOrEqual: Result := TScalar.FromBoolean(leftScalar >= rightScalar); - else - raise ENotSupportedException.Create('Unsupported binary operator.'); - end; + var res: TScalar; + if not TScalar.TryBinaryOperation(Node.Operator, leftValue.AsScalar, rightValue.AsScalar, res) then + raise ENotSupportedException.Create( + 'Binary operation not supported for scalar types ' + + leftValue.AsScalar.Kind.ToString + + ' and ' + + rightValue.AsScalar.Kind.ToString + + ' .'); + Result := res; end; function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; var rightValue: TDataValue; - rightScalar: TScalar; begin rightValue := Node.Right.Accept(Self); - if not TScalar.IsUnaryOperatorSupported(Node.Operator, rightScalar.Kind) then - raise ENotSupportedException.Create('Unary operation not supported for scalar type.'); + if rightValue.Kind <> vkScalar then + raise ENotSupportedException.Create('Unary operations are only supported for scalar types.'); - case Node.Operator of - uoNegate: - begin - rightScalar := rightValue.AsScalar; - Result := -rightScalar; - end; - uoNot: - begin - Result := TScalar.FromBoolean(not IsTruthy(rightValue)); - end; - else - raise ENotSupportedException.Create('Unary operator not supported'); - end; + var res: TScalar; + if not TScalar.TryUnaryOperation(Node.Operator, rightValue.AsScalar, res) then + raise ENotSupportedException.Create('Unary operation not supported for scalar type' + rightValue.AsScalar.Kind.ToString + '.'); + Result := res; end; function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; diff --git a/Src/Data/Myc.Data.Scalar.pas b/Src/Data/Myc.Data.Scalar.pas index 2cdb074..bd4fa3f 100644 --- a/Src/Data/Myc.Data.Scalar.pas +++ b/Src/Data/Myc.Data.Scalar.pas @@ -100,6 +100,9 @@ type class function IsBinaryOperatorSupported(Op: TBinaryOperator; A, B: TScalarKind): Boolean; static; class function IsUnaryOperatorSupported(Op: TUnaryOperator; A: TScalarKind): Boolean; static; + class function TryBinaryOperation(Op: TBinaryOperator; const A, B: TScalar; out Res: TScalar): Boolean; static; + class function TryUnaryOperation(Op: TUnaryOperator; const A: TScalar; out Res: TScalar): Boolean; static; + class operator Add(const A, B: TScalar): TScalar; class operator Divide(const A, B: TScalar): TScalar; class operator Equal(const A, B: TScalar): Boolean; @@ -107,6 +110,7 @@ type class operator GreaterThanOrEqual(const A, B: TScalar): Boolean; class operator LessThan(const A, B: TScalar): Boolean; class operator LessThanOrEqual(const A, B: TScalar): Boolean; + class operator LogicalNot(const A: TScalar): TScalar; class operator Multiply(const A, B: TScalar): TScalar; class operator Negative(const A: TScalar): TScalar; class operator NotEqual(const A, B: TScalar): Boolean; @@ -471,31 +475,31 @@ end; class function TScalar.StringToKind(const AName: string): TScalarKind; begin - if SameText(AName, 'integer') then + if SameText(AName, 'Integer') then Result := skInteger - else if SameText(AName, 'int64') then + else if SameText(AName, 'Int64') then Result := skInt64 - else if SameText(AName, 'uint64') then + else if SameText(AName, 'UInt64') then Result := skUInt64 - else if SameText(AName, 'single') then + else if SameText(AName, 'Single') then Result := skSingle - else if SameText(AName, 'double') then + else if SameText(AName, 'Double') then Result := skDouble - else if SameText(AName, 'datetime') then + else if SameText(AName, 'DateTime') then Result := skDateTime - else if SameText(AName, 'timestamp') then + else if SameText(AName, 'Timestamp') then Result := skTimestamp - else if SameText(AName, 'boolean') then + else if SameText(AName, 'Boolean') then Result := skBoolean - else if SameText(AName, 'char') then + else if SameText(AName, 'Char') then Result := skChar - else if SameText(AName, 'pchar') then + else if SameText(AName, 'PChar') then Result := skPChar - else if SameText(AName, 'string') then + else if SameText(AName, 'String') then Result := skString - else if SameText(AName, 'bytes') then + else if SameText(AName, 'Bytes') then Result := skBytes - else if SameText(AName, 'decimal') then + else if SameText(AName, 'Decimal') then Result := skDecimal else raise EArgumentException.CreateFmt('Unknown scalar type name: "%s"', [AName]); @@ -522,6 +526,58 @@ begin end; end; +class function TScalar.TryBinaryOperation(Op: TBinaryOperator; const A, B: TScalar; out Res: TScalar): Boolean; +begin + if not IsBinaryOperatorSupported(Op, A.Kind, B.Kind) then + begin + Result := False; + exit; + end; + + try + case Op of + boAdd: Res := A + B; + boSubtract: Res := A - B; + boMultiply: Res := A * B; + boDivide: Res := A / B; + boEqual: Res := TScalar.FromBoolean(A = B); + boNotEqual: Res := TScalar.FromBoolean(A <> B); + boLess: Res := TScalar.FromBoolean(A < B); + boGreater: Res := TScalar.FromBoolean(A > B); + boLessOrEqual: Res := TScalar.FromBoolean(A <= B); + boGreaterOrEqual: Res := TScalar.FromBoolean(A >= B); + else + Result := False; + exit; + end; + Result := True; + except + Result := False; + end; +end; + +class function TScalar.TryUnaryOperation(Op: TUnaryOperator; const A: TScalar; out Res: TScalar): Boolean; +begin + if not IsUnaryOperatorSupported(Op, A.Kind) then + begin + Result := False; + exit; + end; + + try + case Op of + uoNegate: Res := -A; + uoNot: Res := not A; + else + Result := False; + exit; + end; + Result := True; + except + Result := False; + end; +end; + class operator TScalar.Add(const A, B: TScalar): TScalar; begin // Fast path for identical types @@ -916,6 +972,19 @@ begin Result := (A < B) or (A = B); end; +class operator TScalar.LogicalNot(const A: TScalar): TScalar; +begin + Result.Kind := A.Kind; + case A.Kind of + skBoolean: Result.Value.AsBoolean := not A.Value.AsBoolean; + skInteger: Result.Value.AsInteger := not A.Value.AsInteger; + skInt64: Result.Value.AsInt64 := not A.Value.AsInt64; + skUInt64: Result.Value.AsUInt64 := not A.Value.AsUInt64; + else + raise EArgumentException.CreateFmt('Operator Not not supported for type %s', [A.Kind.ToString]); + end; +end; + class operator TScalar.Multiply(const A, B: TScalar): TScalar; begin // Fast path for identical types