unit Myc.Data.Value; interface uses System.SysUtils, Myc.Data.Scalar, Myc.Data.Series; type TDataValueKind = (vkVoid, vkScalar, vkInterface, vkText, vkSeries, vkRecordSeries, vkRecord, vkMemberSeries, vkGeneric, vkMethod); TDataValue = record public type TVal = class(TInterfacedObject) Value: T; constructor Create(const AValue: T); end; TFunc = reference to function(const ArgNodes: TArray): TDataValue; private var FKind: TDataValueKind; FScalar: TScalar; FInterface: IInterface; function GetKind: TDataValueKind; inline; function GetIsVoid: Boolean; inline; public class operator Initialize(out Dest: TDataValue); class function Void: TDataValue; inline; static; class operator Implicit(const AValue: TScalar): TDataValue; overload; inline; class operator Implicit(const AValue: String): TDataValue; overload; inline; class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline; class function From(const [ref] AValue: T): TDataValue; static; inline; function AsVal: TVal; inline; class function FromSeries(const [ref] AValue: TScalarSeries): TDataValue; static; inline; class function FromRecordSeries(const [ref] AValue: TScalarRecordSeries): TDataValue; static; inline; class function FromRecord(const [ref] AValue: TScalarRecord): TDataValue; static; inline; class function FromMemberSeries(const [ref] AValue: TScalarMemberSeries): TDataValue; static; inline; class function FromInterface(const [ref] AValue: IInterface): TDataValue; static; inline; function AsScalar: TScalar; inline; function AsInterface: IInterface; inline; function AsMethod: TFunc; inline; function AsText: String; inline; function AsRecordSeries: TVal; inline; function AsRecord: TVal; inline; function AsMemberSeries: TVal; inline; function AsSeries: TVal; inline; procedure SetVoid; function ToString: String; property IsVoid: Boolean read GetIsVoid; property Kind: TDataValueKind read GetKind; end; implementation uses TypInfo; { TDataValue.TVal } constructor TDataValue.TVal.Create(const AValue: T); begin inherited Create; Value := AValue; end; { TDataValue } class operator TDataValue.Initialize(out Dest: TDataValue); begin Dest.FKind := vkVoid; Dest.FInterface := nil; end; function TDataValue.AsInterface: IInterface; begin if (FKind <> vkInterface) then raise EInvalidCast.Create('Cannot read value as an Interface.'); Result := FInterface; end; function TDataValue.AsMemberSeries: TVal; begin if (FKind <> vkMemberSeries) then raise EInvalidCast.Create('Cannot read value as MemberSeries.'); Result := TVal(FInterface); end; function TDataValue.AsMethod: TFunc; begin if (FKind <> vkMethod) then raise EInvalidCast.Create('Cannot read value as a method.'); Result := TFunc(FInterface); end; function TDataValue.AsVal: TVal; begin if (FKind <> vkGeneric) then raise EInvalidCast.Create('Cannot read value as ' + String(PTypeInfo(TypeInfo(T)).Name) + '.'); Result := TVal(FInterface); end; function TDataValue.AsRecord: TVal; begin if (FKind <> vkRecord) then raise EInvalidCast.Create('Cannot read value as Record.'); Result := (FInterface as TVal); end; function TDataValue.AsRecordSeries: TVal; begin if (FKind <> vkRecordSeries) then raise EInvalidCast.Create('Cannot read value as RecordSeries.'); Result := TVal(FInterface); end; function TDataValue.AsScalar: TScalar; begin if (FKind <> vkScalar) then raise EInvalidCast.Create('Cannot read value as a Scalar.'); Result := FScalar; end; function TDataValue.AsSeries: TVal; begin if (FKind <> vkSeries) then raise EInvalidCast.Create('Cannot read value as Series.'); Result := TVal(FInterface); end; function TDataValue.AsText: String; begin if (FKind <> vkText) then raise EInvalidCast.Create('Cannot read value as Text.'); Result := (FInterface as TVal).Value; end; class function TDataValue.From(const [ref] AValue: T): TDataValue; begin Result.FKind := vkGeneric; Result.FInterface := TVal.Create(AValue); end; class function TDataValue.FromInterface(const [ref] AValue: IInterface): TDataValue; begin Result.FKind := vkInterface; Result.FInterface := AValue; end; class function TDataValue.FromMemberSeries(const [ref] AValue: TScalarMemberSeries): TDataValue; begin Result.FKind := vkMemberSeries; Result.FInterface := TVal.Create(AValue); end; class function TDataValue.FromRecord(const [ref] AValue: TScalarRecord): TDataValue; begin Result.FKind := vkRecord; Result.FInterface := TVal.Create(AValue); end; class function TDataValue.FromRecordSeries(const [ref] AValue: TScalarRecordSeries): TDataValue; begin Result.FKind := vkRecordSeries; Result.FInterface := TVal.Create(AValue); end; class function TDataValue.FromSeries(const [ref] AValue: TScalarSeries): TDataValue; begin Result.FKind := vkSeries; Result.FInterface := TVal.Create(AValue); end; class operator TDataValue.Implicit(const AValue: TScalar): TDataValue; begin Result.FKind := vkScalar; Result.FScalar := AValue; Result.FInterface := nil; end; class operator TDataValue.Implicit(const AValue: String): TDataValue; begin Result.FKind := vkText; Result.FInterface := TVal.Create(AValue); end; function TDataValue.GetIsVoid: Boolean; begin Result := FKind = vkVoid; end; function TDataValue.GetKind: TDataValueKind; begin Result := FKind; end; procedure TDataValue.SetVoid; begin FKind := vkVoid; FInterface := nil; end; function TDataValue.ToString: String; var sb: TStringBuilder; field: TScalarRecordField; first: Boolean; begin case FKind of vkScalar: Result := FScalar.ToString; vkInterface: Result := ''; vkText: Result := '"' + AsText + '"'; vkSeries: begin var series := AsSeries.Value; // Add type and count for series Result := Format('[%d]>', [series.Kind.ToString, series.Items.Count]); end; vkRecordSeries: begin var series := AsRecordSeries.Value; sb := TStringBuilder.Create; try sb.Append('{'); first := True; for field in series.Def.Fields do begin if not first then sb.Append(','); sb.Append(field.Name); first := False; end; sb.Append('}'); // Add field names and count for record series Result := Format('', [sb.ToString, series.Count]); finally sb.Free; end; end; vkRecord: begin var rec := AsRecord.Value; sb := TStringBuilder.Create; try sb.Append('{'); first := True; for field in rec.Def.Fields do begin if not first then sb.Append(','); sb.Append(field.Name); first := False; end; sb.Append('}'); // Add field names for records Result := Format('', [sb.ToString]); finally sb.Free; end; end; vkMemberSeries: begin var series := AsMemberSeries.Value; // Add type and count for member series Result := Format('[%d]>', [series.Kind.ToString, series.Count]); end; vkVoid: Result := ''; vkMethod: Result := ''; vkGeneric: // Getting meaningful type information for vkGeneric is not easily possible // without storing additional RTTI, as the specific type of T in TVal is lost. Result := ''; else Result := '[Unknown DataValue]'; end; end; class function TDataValue.Void: TDataValue; begin Result := Default(TDataValue); end; class operator TDataValue.Implicit(const AValue: TDataValue.TFunc): TDataValue; begin Result.FKind := vkMethod; TFunc(Result.FInterface) := AValue; end; end.