diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 733318a..39eece6 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -4,7 +4,7 @@ 20.3 FMX True - Release + Debug Win64 ASTPlayground 2 diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 751745d..1c7db9a 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -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; diff --git a/Src/AST/Myc.Ast.RTL.pas b/Src/AST/Myc.Ast.RTL.pas index 1f60f6c..ef34b3c 100644 --- a/Src/AST/Myc.Ast.RTL.pas +++ b/Src/AST/Myc.Ast.RTL.pas @@ -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; + 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; +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 diff --git a/Src/Data/Myc.Data.Scalar.pas b/Src/Data/Myc.Data.Scalar.pas index ebb0ec9..98ba69c 100644 --- a/Src/Data/Myc.Data.Scalar.pas +++ b/Src/Data/Myc.Data.Scalar.pas @@ -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; 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; diff --git a/Src/Data/Myc.Data.Value.pas b/Src/Data/Myc.Data.Value.pas index 3f1a8bc..1c5a317 100644 --- a/Src/Data/Myc.Data.Value.pas +++ b/Src/Data/Myc.Data.Value.pas @@ -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.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('[%d]>', [series.Kind.ToString, series.Count]); + Result := Format('', [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('[%d]>', [series.Kind.ToString, series.Count]); - end; Ord(vkVoid): Result := ''; Ord(vkMethod): Result := ''; 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';