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';