Streamlining dataflow

This commit is contained in:
Michael Schimmel
2025-09-18 14:23:19 +02:00
parent 2c55a120f1
commit 1be9591a61
5 changed files with 84 additions and 88 deletions
+1 -37
View File
@@ -234,19 +234,6 @@ begin
end;
case seriesVar.Kind of
vkSeries:
begin
if (itemValue.Kind <> vkScalar) then
raise EArgumentException.Create('Can only add scalar values to a TScalarSeries.');
with seriesVar.AsSeries do
begin
if (itemValue.AsScalar.Kind <> Kind) then
raise EArgumentException
.CreateFmt('Type mismatch: Cannot add %s to a series of %s.', [itemValue.AsScalar.Kind.ToString, Kind.ToString]);
Add(itemValue.AsScalar.Value, lookback);
end;
end;
vkRecordSeries:
begin
if (itemValue.Kind <> vkRecord) then
@@ -340,14 +327,6 @@ begin
var recordValue := series.Items[Integer(index)];
Result := TDataValue.FromRecord(recordValue);
end;
vkMemberSeries:
begin
var memberSeries := baseValue.AsMemberSeries;
if (index < 0) or (index >= memberSeries.Count) then
raise EArgumentException
.CreateFmt('Index %d is out of bounds for member series with %d elements.', [index, memberSeries.Count]);
Result := memberSeries.Items[Integer(index)];
end;
else
raise EArgumentException.Create('Indexer `[]` is not supported for this value type.');
end;
@@ -362,21 +341,7 @@ begin
memberName := Node.Member.Name;
case baseValue.Kind of
vkSeries:
begin
with baseValue.AsSeries do
begin
if SameText(memberName, 'Count') then
Result := TScalar.FromInt64(Count)
else if SameText(memberName, 'TotalCount') then
Result := TScalar.FromInt64(TotalCount)
else if SameText(memberName, 'Kind') then
Result := Kind.ToString
else
raise EArgumentException.CreateFmt('Member "%s" not found on TScalarSeries.', [memberName]);
end;
end;
vkRecordSeries: Result := TDataValue.FromMemberSeries(baseValue.AsRecordSeries.CreateMemberSeries(memberName));
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries.CreateMemberSeries(memberName));
vkRecord: Result := baseValue.AsRecord.Items[memberName];
else
raise EArgumentException.Create('Member access operator `.` is not supported for this value type.');
@@ -462,7 +427,6 @@ begin
case seriesValue.Kind of
vkSeries: len := seriesValue.AsSeries.Count;
vkRecordSeries: len := seriesValue.AsRecordSeries.Count;
vkMemberSeries: len := seriesValue.AsMemberSeries.Count;
else
raise EArgumentException.CreateFmt('Cannot get length of type %s.', [GetEnumName(TypeInfo(TDataValueKind), Ord(seriesValue.Kind))]);
end;
+73 -3
View File
@@ -5,8 +5,6 @@ interface
// This unit is intended to be included in a 'uses' clause.
// It self-registers its functions via its initialization section.
implementation
uses
System.SysUtils,
System.Math,
@@ -17,6 +15,58 @@ uses
Myc.Ast.Nodes,
Myc.Ast.Scope;
implementation
type
// Implements a lazily evaluated, read-only series based on a source series and a mapper function.
TMapSeries = class(TInterfacedObject, ISeries)
private
FSourceSeries: ISeries;
FMapperFunc: TDataValue.TFunc;
// ISeries
function GetCount: Int64;
function GetTotalCount: Int64;
function GetItems(Idx: Integer): TScalar;
public
constructor Create(const ASourceSeries: ISeries; const AMapperFunc: TDataValue.TFunc);
end;
{ TMapSeries }
constructor TMapSeries.Create(const ASourceSeries: ISeries; const AMapperFunc: TDataValue.TFunc);
begin
inherited Create;
FSourceSeries := ASourceSeries;
FMapperFunc := AMapperFunc;
end;
function TMapSeries.GetCount: Int64;
begin
Result := FSourceSeries.Count;
end;
function TMapSeries.GetTotalCount: Int64;
begin
Result := FSourceSeries.TotalCount;
end;
function TMapSeries.GetItems(Idx: Integer): TScalar;
var
sourceValue: TScalar;
argArray: TArray<TDataValue>;
mappedValue: TDataValue;
begin
// Get the original item, apply the mapper function, and return the result.
sourceValue := FSourceSeries.Items[Idx];
argArray := [TDataValue(sourceValue)];
mappedValue := FMapperFunc(argArray);
if mappedValue.Kind <> vkScalar then
raise EInvalidCast.Create('Map function did not return a scalar value.');
Result := mappedValue.AsScalar;
end;
//--------------------------------------------------------------------------------------------------
//== Native Function Implementations
//--------------------------------------------------------------------------------------------------
@@ -77,6 +127,7 @@ begin
skInteger, skInt64: Result := arg; // Ceil on an integer is a no-op
skSingle: Result := TScalar.FromInt64(Ceil(arg.Value.AsSingle));
skDouble: Result := TScalar.FromInt64(Ceil(arg.Value.AsDouble));
skDecimal: Result := TScalar.FromInt64(Ceil(Double(arg.Value.AsDecimal)));
else
raise EArgumentException.Create('Ceil requires a numeric argument.');
end;
@@ -91,7 +142,7 @@ begin
skInteger, skInt64: Result := arg; // Floor on an integer is a no-op
skSingle: Result := TScalar.FromInt64(Floor(arg.Value.AsSingle));
skDouble: Result := TScalar.FromInt64(Floor(arg.Value.AsDouble));
skDecimal: Result := TScalar.FromInt64(Trunc(arg.Value.AsDecimal));
skDecimal: Result := TScalar.FromInt64(Floor(Double(arg.Value.AsDecimal)));
else
raise EArgumentException.Create('Floor requires a numeric argument.');
end;
@@ -113,6 +164,24 @@ begin
end;
end;
// Maps a series to a new series by applying a function to each element.
function NativeMap(const Args: TArray<TDataValue>): TDataValue;
var
sourceArg: TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Map requires exactly two arguments: a series and a function.');
sourceArg := Args[0];
if not (sourceArg.Kind in [vkSeries]) then
raise EArgumentException.Create('The first argument to Map must be a series.');
if Args[1].Kind <> vkMethod then
raise EArgumentException.Create('The second argument to Map must be a function.');
Result := TDataValue.FromSeries(TMapSeries.Create(sourceArg.AsSeries, Args[1].AsMethod()));
end;
//--------------------------------------------------------------------------------------------------
//== Library Registration
//--------------------------------------------------------------------------------------------------
@@ -124,6 +193,7 @@ begin
AScope.Define('Ceil', TDataValue(NativeCeil));
AScope.Define('Floor', TDataValue(NativeFloor));
AScope.Define('Sign', TDataValue(NativeSign));
AScope.Define('Map', TDataValue(NativeMap));
end;
initialization
-14
View File
@@ -177,12 +177,10 @@ type
{$region 'private'}
function GetCount: Int64;
function GetItems(Idx: Integer): TScalar;
function GetKind: TScalarKind;
function GetTotalCount: Int64;
{$endregion}
property Count: Int64 read GetCount;
property Items[Idx: Integer]: TScalar read GetItems; default;
property Kind: TScalarKind read GetKind;
property TotalCount: Int64 read GetTotalCount;
end;
@@ -197,7 +195,6 @@ type
FArray: TChunkArray<TScalarValue>;
FTotalCount: Int64;
function GetKind: TScalarKind; inline;
function GetCount: Int64; inline;
function GetItems(Idx: Integer): TScalar; inline;
function GetTotalCount: Int64; inline;
@@ -232,7 +229,6 @@ type
FOffset: Integer;
function GetCount: Int64;
function GetItems(Idx: Integer): TScalar;
function GetKind: TScalarKind;
function GetTotalCount: Int64;
public
constructor Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
@@ -1524,11 +1520,6 @@ begin
Result.Create(FKind, P^);
end;
function TScalarRecordSeries.TMemberSeries.GetKind: TScalarKind;
begin
Result := FKind;
end;
function TScalarRecordSeries.TMemberSeries.GetTotalCount: Int64;
begin
Result := FRecordSeries.TotalCount;
@@ -1561,11 +1552,6 @@ begin
Result.Create(FKind, FArray[FArray.Count - Idx - 1]);
end;
function TScalarSeries.GetKind: TScalarKind;
begin
Result := FKind;
end;
function TScalarSeries.GetTotalCount: Int64;
begin
Result := FTotalCount;
+9 -33
View File
@@ -8,7 +8,7 @@ uses
Myc.Data.Series;
type
TDataValueKind = (vkVoid, vkScalar, vkText, vkSeries, vkRecordSeries, vkRecord, vkMemberSeries, vkMethod, vkGeneric);
TDataValueKind = (vkVoid, vkScalar, vkText, vkSeries, vkRecordSeries, vkRecord, vkMethod, vkGeneric);
TDataValue = record
type
@@ -63,18 +63,16 @@ type
class function Defer(const Value: TDataValue; const Calc: TFunc): TDataValue; overload; static;
class function FromSeries(const AValue: IWriteableSeries): TDataValue; static; inline;
class function FromRecordSeries(const AValue: IRecordSeries): TDataValue; static; inline;
class function FromRecord(const [ref] AValue: TScalarRecord): TDataValue; static; inline;
class function FromMemberSeries(const AValue: ISeries): TDataValue; static; inline;
class function FromSeries(const AValue: ISeries): TDataValue; static; inline;
function AsScalar: TScalar; inline;
function AsMethod: TFunc; inline;
function AsText: String; inline;
function AsRecordSeries: IRecordSeries; inline;
function AsRecord: TScalarRecord; inline;
function AsMemberSeries: ISeries; inline;
function AsSeries: IWriteableSeries; inline;
function AsSeries: ISeries; inline;
function ToString: String;
property IsVoid: Boolean read GetIsVoid;
@@ -186,11 +184,11 @@ begin
Dest.FInterface := nil;
end;
function TDataValue.AsMemberSeries: ISeries;
function TDataValue.AsSeries: ISeries;
begin
if FKind = Ord(vkLazy) then
exit(AsLazy.Value.AsMemberSeries);
if (FKind <> Ord(vkMemberSeries)) then
exit(AsLazy.Value.AsSeries);
if (FKind <> Ord(vkSeries)) then
raise EInvalidCast.Create('Cannot read value as MemberSeries.');
Result := ISeries(FInterface);
end;
@@ -244,15 +242,6 @@ begin
Result := FScalar;
end;
function TDataValue.AsSeries: IWriteableSeries;
begin
if FKind = Ord(vkLazy) then
exit(AsLazy.Value.AsSeries);
if (FKind <> Ord(vkSeries)) then
raise EInvalidCast.Create('Cannot read value as Series.');
Result := IWriteableSeries(FInterface);
end;
function TDataValue.AsText: String;
begin
if FKind = Ord(vkLazy) then
@@ -297,9 +286,9 @@ begin
Result.FInterface := TVal<T>.Create(AValue);
end;
class function TDataValue.FromMemberSeries(const AValue: ISeries): TDataValue;
class function TDataValue.FromSeries(const AValue: ISeries): TDataValue;
begin
Result.FKind := Ord(vkMemberSeries);
Result.FKind := Ord(vkSeries);
Result.FInterface := AValue;
end;
@@ -315,12 +304,6 @@ begin
Result.FInterface := AValue;
end;
class function TDataValue.FromSeries(const AValue: IWriteableSeries): TDataValue;
begin
Result.FKind := Ord(vkSeries);
Result.FInterface := AValue;
end;
class operator TDataValue.Implicit(const AValue: TScalar): TDataValue;
begin
Result.FKind := Ord(vkScalar);
@@ -359,7 +342,7 @@ begin
begin
var series := AsSeries;
// Add type and count for series
Result := Format('<series<%s>[%d]>', [series.Kind.ToString, series.Count]);
Result := Format('<series[%d]>', [series.Count]);
end;
Ord(vkRecordSeries):
begin
@@ -403,12 +386,6 @@ begin
sb.Free;
end;
end;
Ord(vkMemberSeries):
begin
var series := AsMemberSeries;
// Add type and count for member series
Result := Format('<member_series<%s>[%d]>', [series.Kind.ToString, series.Count]);
end;
Ord(vkVoid): Result := '<void>';
Ord(vkMethod): Result := '<method>';
Ord(vkGeneric):
@@ -447,7 +424,6 @@ begin
vkSeries: Result := 'Series';
vkRecordSeries: Result := 'RecordSeries';
vkRecord: Result := 'Record';
vkMemberSeries: Result := 'MemberSeries';
vkMethod: Result := 'Method';
else
Result := 'unknown';