From a83d3bf9fccec2607babee08f3ae5d1b44182194 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Mon, 15 Sep 2025 16:44:40 +0200 Subject: [PATCH] Scalar operators --- Src/AST/Myc.Ast.Evaluator.pas | 105 ++++++++-------------------------- Src/AST/Myc.Ast.Nodes.pas | 4 -- Src/Data/Myc.Data.Scalar.pas | 67 +++++++++++++++++++++- 3 files changed, 89 insertions(+), 87 deletions(-) diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index b3884fa..cb4d533 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -385,15 +385,11 @@ begin end; function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; -var - value: TDataValue; begin if Assigned(Node.Initializer) then - value := Node.Initializer.Accept(Self); + Result := Node.Initializer.Accept(Self); - FScope[Node.Identifier.Address] := value; - - Result := value; + FScope[Node.Identifier.Address] := Result; end; function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; @@ -410,58 +406,22 @@ begin leftScalar := leftValue.AsScalar; rightScalar := rightValue.AsScalar; - if (leftScalar.Kind <> rightScalar.Kind) then - begin - if (leftScalar.Kind = skInt64) and (rightScalar.Kind = skDouble) then - leftScalar := TScalar.FromDouble(leftScalar.Value.AsInt64) - else if (leftScalar.Kind = skDouble) and (rightScalar.Kind = skInt64) then - rightScalar := TScalar.FromDouble(rightScalar.Value.AsInt64) - else - raise ENotSupportedException.Create( - 'Binary operations are only supported for compatible types. ' + leftScalar.ToString + ' ' + rightScalar.ToString); - end; + if not TScalar.IsBinaryOperatorSupported(Node.Operator, leftScalar.Kind, rightScalar.Kind) then + raise ENotSupportedException.Create('Binary operation not supported for scalar type.'); - case leftScalar.Kind of - skInt64: - begin - var leftVal := leftScalar.Value.AsInt64; - var rightVal := rightScalar.Value.AsInt64; - case Node.Operator of - boAdd: Result := TScalar.FromInt64(leftVal + rightVal); - boSubtract: Result := TScalar.FromInt64(leftVal - rightVal); - boMultiply: Result := TScalar.FromInt64(leftVal * rightVal); - boDivide: Result := TScalar.FromInt64(leftVal div rightVal); - boEqual: Result := TScalar.FromBoolean(leftVal = rightVal); - boNotEqual: Result := TScalar.FromBoolean(leftVal <> rightVal); - boLess: Result := TScalar.FromBoolean(leftVal < rightVal); - boGreater: Result := TScalar.FromBoolean(leftVal > rightVal); - boLessOrEqual: Result := TScalar.FromBoolean(leftVal <= rightVal); - boGreaterOrEqual: Result := TScalar.FromBoolean(leftVal >= rightVal); - else - raise ENotSupportedException.Create('Operator not supported for Int64.'); - end; - end; - skDouble: - begin - var leftVal := leftScalar.Value.AsDouble; - var rightVal := rightScalar.Value.AsDouble; - case Node.Operator of - boAdd: Result := TScalar.FromDouble(leftVal + rightVal); - boSubtract: Result := TScalar.FromDouble(leftVal - rightVal); - boMultiply: Result := TScalar.FromDouble(leftVal * rightVal); - boDivide: Result := TScalar.FromDouble(leftVal / rightVal); - boEqual: Result := TScalar.FromBoolean(leftVal = rightVal); - boNotEqual: Result := TScalar.FromBoolean(leftVal <> rightVal); - boLess: Result := TScalar.FromBoolean(leftVal < rightVal); - boGreater: Result := TScalar.FromBoolean(leftVal > rightVal); - boLessOrEqual: Result := TScalar.FromBoolean(leftVal <= rightVal); - boGreaterOrEqual: Result := TScalar.FromBoolean(leftVal >= rightVal); - else - raise ENotSupportedException.Create('Operator not supported for Double.'); - end; - end; + 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('Binary operations are not supported for this scalar type.'); + raise ENotSupportedException.Create('Unsupported binary operator.'); end; end; @@ -472,18 +432,14 @@ var 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.'); + case Node.Operator of uoNegate: begin - if (rightValue.Kind <> vkScalar) then - raise ENotSupportedException.Create('Unary "-" is only supported for scalar types.'); rightScalar := rightValue.AsScalar; - case rightScalar.Kind of - skInt64: Result := TScalar.FromInt64(-rightScalar.Value.AsInt64); - skDouble: Result := TScalar.FromDouble(-rightScalar.Value.AsDouble); - else - raise ENotSupportedException.Create('Unary "-" is not supported for this scalar type.'); - end; + Result := -rightScalar; end; uoNot: begin @@ -495,27 +451,16 @@ begin end; function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; -var - conditionValue: TDataValue; begin - conditionValue := Node.Condition.Accept(Self); - if IsTruthy(conditionValue) then + if IsTruthy(Node.Condition.Accept(Self)) then Result := Node.ThenBranch.Accept(Self) - else - begin - if Assigned(Node.ElseBranch) then - Result := Node.ElseBranch.Accept(Self) - else - Result := TDataValue.Void; - end; + else if Assigned(Node.ElseBranch) then + Result := Node.ElseBranch.Accept(Self); end; function TEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; -var - conditionValue: TDataValue; begin - conditionValue := Node.Condition.Accept(Self); - if IsTruthy(conditionValue) then + if IsTruthy(Node.Condition.Accept(Self)) then Result := Node.ThenBranch.Accept(Self) else Result := Node.ElseBranch.Accept(Self); @@ -527,9 +472,7 @@ var begin Result := TDataValue.Void; for expression in Node.Expressions do - begin Result := expression.Accept(Self); - end; end; function TEvaluatorVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 135abfb..b8bdb3f 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -9,10 +9,6 @@ uses Myc.Data.Value; type - // Operators and helpers - TBinaryOperator = (boAdd, boSubtract, boMultiply, boDivide, boEqual, boNotEqual, boLess, boGreater, boLessOrEqual, boGreaterOrEqual); - TUnaryOperator = (uoNegate, uoNot); - TBinaryOperatorHelper = record helper for TBinaryOperator function ToString: string; end; diff --git a/Src/Data/Myc.Data.Scalar.pas b/Src/Data/Myc.Data.Scalar.pas index b42933d..2cdb074 100644 --- a/Src/Data/Myc.Data.Scalar.pas +++ b/Src/Data/Myc.Data.Scalar.pas @@ -26,6 +26,9 @@ type skDecimal ); + TBinaryOperator = (boAdd, boSubtract, boMultiply, boDivide, boEqual, boNotEqual, boLess, boGreater, boLessOrEqual, boGreaterOrEqual); + TUnaryOperator = (uoNegate, uoNot); + TScalarBytes = array[0..7] of Byte; TScalarPChar = array[0..3] of Char; TScalarString = String[7]; @@ -94,11 +97,12 @@ type function ToString: String; + class function IsBinaryOperatorSupported(Op: TBinaryOperator; A, B: TScalarKind): Boolean; static; + class function IsUnaryOperatorSupported(Op: TUnaryOperator; A: TScalarKind): 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; - - //TODO implementieren class operator GreaterThan(const A, B: TScalar): Boolean; class operator GreaterThanOrEqual(const A, B: TScalar): Boolean; class operator LessThan(const A, B: TScalar): Boolean; @@ -406,6 +410,65 @@ begin Result.Value := TScalarValue.FromUInt64(AValue); end; +class function TScalar.IsBinaryOperatorSupported(Op: TBinaryOperator; A, B: TScalarKind): Boolean; +begin + const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble]; + const ORDINAL_KINDS: set of TScalarKind = [skInteger, skInt64, skUInt64]; + const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + ORDINAL_KINDS + [skDecimal]; + const COMPARABLE_KINDS: set of TScalarKind = NUMERIC_KINDS + [skDateTime, skChar, skString]; + + // General check for incompatible types + if (A = B) then + begin + case Op of + boAdd, boSubtract, boMultiply, boDivide: Result := A in NUMERIC_KINDS + [skDateTime]; + boEqual, boNotEqual, boLess, boGreater, boLessOrEqual, boGreaterOrEqual: Result := A in COMPARABLE_KINDS; + else + Result := False; + end; + end + else + begin + // Mixed-type checks + case Op of + boAdd: + Result := + (((A in NUMERIC_KINDS) and (B in NUMERIC_KINDS)) + or ((A = skDateTime) and (B in ORDINAL_KINDS + FLOAT_KINDS)) + or ((B = skDateTime) and (A in ORDINAL_KINDS + FLOAT_KINDS))); + boSubtract: + Result := (((A in NUMERIC_KINDS) and (B in NUMERIC_KINDS)) or ((A = skDateTime) and (B in NUMERIC_KINDS + [skDateTime]))); + boMultiply, boDivide: Result := (A in NUMERIC_KINDS) and (B in NUMERIC_KINDS); + boEqual, boNotEqual, boLess, boGreater, boLessOrEqual, boGreaterOrEqual: + Result := (A in NUMERIC_KINDS) and (B in NUMERIC_KINDS); + else + Result := False; + end; + end; + + // Specific exclusion rules + if Result then + begin + // Decimal cannot be mixed with floats for any operator + if ((A = skDecimal) and (B in FLOAT_KINDS)) or ((B = skDecimal) and (A in FLOAT_KINDS)) then + Result := + False + // Cannot subtract a DateTime from a Numeric type + else if (Op = boSubtract) and (A in NUMERIC_KINDS) and (B = skDateTime) then + Result := False; + end; +end; + +class function TScalar.IsUnaryOperatorSupported(Op: TUnaryOperator; A: TScalarKind): Boolean; +begin + case Op of + uoNegate: Result := A in [skInteger, skInt64, skSingle, skDouble, skDecimal]; + uoNot: Result := A in [skBoolean, skInteger, skInt64, skUInt64]; + else + Result := False; + end; +end; + class function TScalar.StringToKind(const AName: string): TScalarKind; begin if SameText(AName, 'integer') then