299 lines
9.1 KiB
ObjectPascal
299 lines
9.1 KiB
ObjectPascal
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<T> = class(TInterfacedObject)
|
|
Value: T;
|
|
constructor Create(const AValue: T);
|
|
end;
|
|
|
|
TFunc = reference to function(const ArgNodes: TArray<TDataValue>): 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<T: record>(const [ref] AValue: T): TDataValue; static; inline;
|
|
function AsVal<T: record>: TVal<T>; 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<TScalarRecordSeries>; inline;
|
|
function AsRecord: TVal<TScalarRecord>; inline;
|
|
function AsMemberSeries: TVal<TScalarMemberSeries>; inline;
|
|
function AsSeries: TVal<TScalarSeries>; inline;
|
|
|
|
procedure SetVoid;
|
|
|
|
function ToString: String;
|
|
property IsVoid: Boolean read GetIsVoid;
|
|
property Kind: TDataValueKind read GetKind;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
TypInfo;
|
|
|
|
{ TDataValue.TVal<T> }
|
|
|
|
constructor TDataValue.TVal<T>.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<TScalarMemberSeries>;
|
|
begin
|
|
if (FKind <> vkMemberSeries) then
|
|
raise EInvalidCast.Create('Cannot read value as MemberSeries.');
|
|
Result := TVal<TScalarMemberSeries>(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<T>: TVal<T>;
|
|
begin
|
|
if (FKind <> vkGeneric) then
|
|
raise EInvalidCast.Create('Cannot read value as ' + String(PTypeInfo(TypeInfo(T)).Name) + '.');
|
|
Result := TVal<T>(FInterface);
|
|
end;
|
|
|
|
function TDataValue.AsRecord: TVal<TScalarRecord>;
|
|
begin
|
|
if (FKind <> vkRecord) then
|
|
raise EInvalidCast.Create('Cannot read value as Record.');
|
|
Result := (FInterface as TVal<TScalarRecord>);
|
|
end;
|
|
|
|
function TDataValue.AsRecordSeries: TVal<TScalarRecordSeries>;
|
|
begin
|
|
if (FKind <> vkRecordSeries) then
|
|
raise EInvalidCast.Create('Cannot read value as RecordSeries.');
|
|
Result := TVal<TScalarRecordSeries>(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<TScalarSeries>;
|
|
begin
|
|
if (FKind <> vkSeries) then
|
|
raise EInvalidCast.Create('Cannot read value as Series.');
|
|
Result := TVal<TScalarSeries>(FInterface);
|
|
end;
|
|
|
|
function TDataValue.AsText: String;
|
|
begin
|
|
if (FKind <> vkText) then
|
|
raise EInvalidCast.Create('Cannot read value as Text.');
|
|
Result := (FInterface as TVal<String>).Value;
|
|
end;
|
|
|
|
class function TDataValue.From<T>(const [ref] AValue: T): TDataValue;
|
|
begin
|
|
Result.FKind := vkGeneric;
|
|
Result.FInterface := TVal<T>.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<TScalarMemberSeries>.Create(AValue);
|
|
end;
|
|
|
|
class function TDataValue.FromRecord(const [ref] AValue: TScalarRecord): TDataValue;
|
|
begin
|
|
Result.FKind := vkRecord;
|
|
Result.FInterface := TVal<TScalarRecord>.Create(AValue);
|
|
end;
|
|
|
|
class function TDataValue.FromRecordSeries(const [ref] AValue: TScalarRecordSeries): TDataValue;
|
|
begin
|
|
Result.FKind := vkRecordSeries;
|
|
Result.FInterface := TVal<TScalarRecordSeries>.Create(AValue);
|
|
end;
|
|
|
|
class function TDataValue.FromSeries(const [ref] AValue: TScalarSeries): TDataValue;
|
|
begin
|
|
Result.FKind := vkSeries;
|
|
Result.FInterface := TVal<TScalarSeries>.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<String>.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 := '<interface>';
|
|
vkText: Result := '"' + AsText + '"';
|
|
vkSeries:
|
|
begin
|
|
var series := AsSeries.Value;
|
|
// Add type and count for series
|
|
Result := Format('<series<%s>[%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('<record_series%s[%d]>', [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('<record%s>', [sb.ToString]);
|
|
finally
|
|
sb.Free;
|
|
end;
|
|
end;
|
|
vkMemberSeries:
|
|
begin
|
|
var series := AsMemberSeries.Value;
|
|
// Add type and count for member series
|
|
Result := Format('<member_series<%s>[%d]>', [series.Kind.ToString, series.Count]);
|
|
end;
|
|
vkVoid: Result := '<void>';
|
|
vkMethod: Result := '<method>';
|
|
vkGeneric:
|
|
// Getting meaningful type information for vkGeneric is not easily possible
|
|
// without storing additional RTTI, as the specific type of T in TVal<T> is lost.
|
|
Result := '<generic>';
|
|
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.
|