AST Types
This commit is contained in:
+68
-21
@@ -97,9 +97,9 @@ type
|
||||
|
||||
// Defines the rules of the type system.
|
||||
TTypeRules = record
|
||||
private
|
||||
class function Promote(const A, B: IStaticType): IStaticType; static;
|
||||
public
|
||||
class function Promote(const A, B: IStaticType): IStaticType; static;
|
||||
|
||||
// Checks if a value of type 'Source' can be assigned to 'Target'.
|
||||
class function CanAssign(const Target, Source: IStaticType): Boolean; static;
|
||||
// Determines the result type of a binary operation.
|
||||
@@ -450,6 +450,10 @@ begin
|
||||
if (not Assigned(Target)) or (not Assigned(Source)) then
|
||||
exit(False);
|
||||
|
||||
// During inference, allow assignment involving Unknown
|
||||
if (Target.Kind = stUnknown) or (Source.Kind = stUnknown) then
|
||||
exit(True);
|
||||
|
||||
if Target.IsEqual(Source) then
|
||||
exit(True);
|
||||
|
||||
@@ -457,6 +461,9 @@ begin
|
||||
if (Target.Kind = stFloat) and (Source.Kind = stOrdinal) then
|
||||
exit(True);
|
||||
|
||||
if (Target.Kind = stVoid) and (Source.Kind = stMethod) then
|
||||
exit(True);
|
||||
|
||||
// TODO: Implement full assignment compatibility rules (e.g., for records/series)
|
||||
|
||||
Result := False;
|
||||
@@ -464,7 +471,13 @@ end;
|
||||
|
||||
class function TTypeRules.Promote(const A, B: IStaticType): IStaticType;
|
||||
begin
|
||||
// Numeric promotion rule: Float wins
|
||||
// Handle Unknown: If one is Unknown, the result is the other type.
|
||||
if A.Kind = stUnknown then
|
||||
exit(B);
|
||||
if B.Kind = stUnknown then
|
||||
exit(A);
|
||||
|
||||
// Standard Numeric promotion rule: Float wins
|
||||
if (A.Kind = stFloat) and (B.Kind = stFloat) then
|
||||
exit(TTypes.Float);
|
||||
if (A.Kind = stFloat) and (B.Kind = stOrdinal) then
|
||||
@@ -474,28 +487,54 @@ begin
|
||||
if (A.Kind = stOrdinal) and (B.Kind = stOrdinal) then
|
||||
exit(TTypes.Ordinal);
|
||||
|
||||
// If types are identical and not numeric, return that type (e.g. Text + Text might be valid later)
|
||||
if A.IsEqual(B) then
|
||||
exit(A);
|
||||
|
||||
// Cannot promote other combinations during basic inference
|
||||
raise ETypeException.CreateFmt('Cannot promote types %s and %s', [A.ToString, B.ToString]);
|
||||
end;
|
||||
|
||||
class function TTypeRules.ResolveBinaryOp(Op: TScalar.TBinaryOp; const Left, Right: IStaticType): IStaticType;
|
||||
var
|
||||
promotedType: IStaticType;
|
||||
promotedKind: TStaticTypeKind;
|
||||
begin
|
||||
// 1. Determine the effective type after promotion, handling Unknown.
|
||||
try
|
||||
promotedType := Promote(Left, Right);
|
||||
except
|
||||
// If promotion fails (e.g., Text and Ordinal), the operation is invalid.
|
||||
on E: ETypeException do
|
||||
raise ETypeException.CreateFmt(
|
||||
'Operator %s cannot be applied to %s and %s (promotion failed: %s)',
|
||||
[Op.ToString, Left.ToString, Right.ToString, E.Message]);
|
||||
end;
|
||||
|
||||
// If promotion results in Unknown, the result type is also Unknown for now.
|
||||
if promotedType.Kind = stUnknown then
|
||||
exit(TTypes.Unknown);
|
||||
|
||||
// 2. Check if the operator is valid for the promoted type.
|
||||
promotedKind := promotedType.Kind;
|
||||
|
||||
case Op of
|
||||
TScalar.TBinaryOp.Add, TScalar.TBinaryOp.Subtract, TScalar.TBinaryOp.Multiply:
|
||||
begin
|
||||
// Numeric operations: Promote and return
|
||||
if (Left.Kind in [stOrdinal, stFloat]) and (Right.Kind in [stOrdinal, stFloat]) then
|
||||
Result := Promote(Left, Right)
|
||||
else
|
||||
raise ETypeException.CreateFmt('Operator %s cannot be applied to %s and %s', [Op.ToString, Left.ToString, Right.ToString]);
|
||||
// Numeric operations require Ordinal or Float
|
||||
if not (promotedKind in [stOrdinal, stFloat]) then
|
||||
raise ETypeException
|
||||
.CreateFmt('Operator %s requires Ordinal or Float, but got %s after promotion', [Op.ToString, promotedType.ToString]);
|
||||
Result := promotedType; // Result has the promoted type
|
||||
end;
|
||||
|
||||
TScalar.TBinaryOp.Divide:
|
||||
begin
|
||||
// Division *always* results in Float, even Ordinal / Ordinal
|
||||
if (Left.Kind in [stOrdinal, stFloat]) and (Right.Kind in [stOrdinal, stFloat]) then
|
||||
Result := TTypes.Float
|
||||
else
|
||||
raise ETypeException.CreateFmt('Operator %s cannot be applied to %s and %s', [Op.ToString, Left.ToString, Right.ToString]);
|
||||
// Division requires Ordinal or Float, but *always* results in Float
|
||||
if not (promotedKind in [stOrdinal, stFloat]) then
|
||||
raise ETypeException
|
||||
.CreateFmt('Operator %s requires Ordinal or Float, but got %s after promotion', [Op.ToString, promotedType.ToString]);
|
||||
Result := TTypes.Float;
|
||||
end;
|
||||
|
||||
TScalar.TBinaryOp.Equal,
|
||||
@@ -505,12 +544,12 @@ begin
|
||||
TScalar.TBinaryOp.LessOrEqual,
|
||||
TScalar.TBinaryOp.GreaterOrEqual:
|
||||
begin
|
||||
// Comparison operations: Check if promotion is possible, but always return Ordinal (boolean)
|
||||
if (Left.Kind in [stOrdinal, stFloat]) and (Right.Kind in [stOrdinal, stFloat]) then
|
||||
Result := TTypes.Ordinal
|
||||
else
|
||||
raise ETypeException
|
||||
.CreateFmt('Comparison operator %s cannot be applied to %s and %s', [Op.ToString, Left.ToString, Right.ToString]);
|
||||
// Comparison requires Ordinal or Float, but *always* results in Ordinal (boolean)
|
||||
if not (promotedKind in [stOrdinal, stFloat]) then
|
||||
raise ETypeException.CreateFmt(
|
||||
'Comparison operator %s requires Ordinal or Float, but got %s after promotion',
|
||||
[Op.ToString, promotedType.ToString]);
|
||||
Result := TTypes.Ordinal;
|
||||
end;
|
||||
else
|
||||
raise ETypeException.Create('Unknown binary operator for type resolution.');
|
||||
@@ -518,18 +557,26 @@ begin
|
||||
end;
|
||||
|
||||
class function TTypeRules.ResolveUnaryOp(Op: TScalar.TUnaryOp; const Right: IStaticType): IStaticType;
|
||||
var
|
||||
rightKind: TStaticTypeKind;
|
||||
begin
|
||||
rightKind := Right.Kind;
|
||||
|
||||
// If operand is Unknown, result is Unknown.
|
||||
if rightKind = stUnknown then
|
||||
exit(TTypes.Unknown);
|
||||
|
||||
case Op of
|
||||
TScalar.TUnaryOp.Negate:
|
||||
begin
|
||||
if not (Right.Kind in [stOrdinal, stFloat]) then
|
||||
if not (rightKind in [stOrdinal, stFloat]) then
|
||||
raise ETypeException.CreateFmt('Unary negation cannot be applied to %s', [Right.ToString]);
|
||||
Result := Right; // Negation preserves type
|
||||
end;
|
||||
|
||||
TScalar.TUnaryOp.Not:
|
||||
begin
|
||||
if not (Right.Kind = stOrdinal) then
|
||||
if not (rightKind = stOrdinal) then
|
||||
raise ETypeException.CreateFmt('Logical not cannot be applied to %s', [Right.ToString]);
|
||||
Result := TTypes.Ordinal;
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user