Refactoring Keyword mapping, introducing tuples
This commit is contained in:
File diff suppressed because one or more lines are too long
+127
@@ -0,0 +1,127 @@
|
|||||||
|
# Einführung von N-dimensionalen Tupel-Strukturen und Typ-Unification
|
||||||
|
|
||||||
|
**Datum:** 04.01.2026
|
||||||
|
|
||||||
|
**Status:** Design abgeschlossen / Vorbereitung der Implementierung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Motivation
|
||||||
|
|
||||||
|
In der bisherigen Architektur wurden Argumentlisten, Parameterlisten und Datenstrukturen (Records/Series) als getrennte Konzepte behandelt. Dies führt zu unnötigem Overhead durch "Boxing" (Einpacken von Werten in Objekte) und erschwert die statische Optimierung mathematischer Operationen.
|
||||||
|
|
||||||
|
**Das Ziel dieser Erweiterung:**
|
||||||
|
|
||||||
|
* **Vereinheitlichung:** Alles, was eine feste Sequenz von Daten ist, wird intern ein **Tupel**.
|
||||||
|
* **Performance:** Durch präzise statische Analyse der "Shape" (Form) und des Inhalts sollen Daten unboxed (als reine Value-Types) im Speicher liegen können.
|
||||||
|
* **Monomorphisierung:** Der Compiler soll hochoptimierten Maschinencode (SIMD) erzeugen können, sobald er erkennt, dass Datenstrukturen "rechteckig" und homogen sind.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Architekturbeschreibung
|
||||||
|
|
||||||
|
### 2.1 Die Typ-Hierarchie (`IStaticType`)
|
||||||
|
|
||||||
|
Wir führen eine rekursive Inferenz-Logik im `TypeChecker` ein, die den am besten passenden statischen Typ ermittelt:
|
||||||
|
|
||||||
|
1. **`stTuple`**: Der Basisfall. Eine heterogene Sequenz fester Länge (z. B. `[1 "Text"]`). Jeder Slot hat seinen eigenen statischen Typ.
|
||||||
|
2. **`stVector`**: Ein Spezialfall des Tupels. Alle Elemente besitzen den **identischen** statischen Typ (Homogenität). Dies erlaubt den typsicheren Zugriff über variable Indizes.
|
||||||
|
3. **`stMatrix`**: Ein rekursiver Spezialfall des Vektors. Ein Vektor, dessen Elemente wiederum Vektoren oder Matrizen sind, sofern sie eine **identische Shape** (Dimensionen) aufweisen ("Rechteckigkeit").
|
||||||
|
|
||||||
|
### 2.2 Die "Scalar-Pure" Optimierung
|
||||||
|
|
||||||
|
Dies ist eine rein interne Optimierung des Spezialisierers:
|
||||||
|
|
||||||
|
* Wenn ein Typ (`stTuple`, `stVector`, `stMatrix`) ausschließlich statisch bekannte Typen (idealerweise Skalare wie `Ordinal` oder `Float`) enthält, wird er als **Value-Type** behandelt.
|
||||||
|
* **Monomorphisierter Pfad:** Der Spezialisierer erzeugt für diese Strukturen einen dedizierten Code-Pfad, der statt mit langsamen Objekt-Arrays mit flachen, gepackten Speicherblöcken arbeitet.
|
||||||
|
* **Kein implizites Casting:** Um die Inferenz stabil zu halten, findet keine automatische Umwandlung statt (z. B. wird ein `Ordinal` nicht automatisch zu `Float`, um einen Vektor zu erzwingen).
|
||||||
|
|
||||||
|
### 2.3 Unification (Vereinheitlichung der Konzepte)
|
||||||
|
|
||||||
|
Das Tupel-Konzept ersetzt mehrere bisherige Mechanismen:
|
||||||
|
|
||||||
|
* **Argument- & Parameterlisten:** Funktionsaufrufe werden semantisch als Übergabe eines Tupels behandelt. Dies ermöglicht hocheffiziente Registerübergabe.
|
||||||
|
* **Records:** Ein Record ist semantisch nur ein "Tagged Tuple". Die Daten liegen als Tupel vor, ein Keyword-Mapping sorgt lediglich für den namensbasierten Zugriff.
|
||||||
|
* **Multiple Returns:** Funktionen können nativ Tupel zurückgeben, was ohne zusätzliches Heap-Investment verarbeitet werden kann.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Skript-Beispiele und Inferenz
|
||||||
|
|
||||||
|
Die Syntax für alle Tupel-basierten Strukturen ist einheitlich `[...]`.
|
||||||
|
|
||||||
|
### A. Heterogene Strukturen (Tupel)
|
||||||
|
|
||||||
|
```script
|
||||||
|
(def tpl [1 3.14 "text"])
|
||||||
|
; Typ-Inferenz: stTuple<stOrdinal, stFloat, stText>
|
||||||
|
; Spezialisierung: Gepackter Record (Offset 0: Int64, Offset 8: Double, Offset 16: Ptr)
|
||||||
|
|
||||||
|
(get tpl 1) ; -> 3.14 (Der Compiler weiß statisch: Index 1 ist stFloat)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### B. Homogene Strukturen (Vektoren)
|
||||||
|
|
||||||
|
```script
|
||||||
|
(def v [10 20 30])
|
||||||
|
; Typ-Inferenz: stVector<stOrdinal, 3>
|
||||||
|
; Spezialisierung: Flaches Array von 3x Int64 (unboxed, SIMD-optimierbar)
|
||||||
|
|
||||||
|
(def i 2)
|
||||||
|
(get v i) ; -> 30 (Typsicher, da alle Elemente stOrdinal sind)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### C. Multidimensionale Strukturen (Matrizen)
|
||||||
|
|
||||||
|
Die Matrix-Invariante erfordert exakt gleiche Formen der Unterelemente.
|
||||||
|
|
||||||
|
```script
|
||||||
|
; 2D Matrix
|
||||||
|
(def mat2d [[1 2] [3 4]])
|
||||||
|
; Inferenz: stMatrix<stOrdinal, [2, 2]> (Rechteckig -> Optimierung aktiv)
|
||||||
|
|
||||||
|
; 3D Matrix
|
||||||
|
(def mat3d [[[1 2] [3 4]] [[5 6] [7 8]]])
|
||||||
|
; Inferenz: stMatrix<stOrdinal, [2, 2, 2]> (Linearisierter Speicherblock)
|
||||||
|
|
||||||
|
; Degradiertes Tupel (Shape-Mismatch)
|
||||||
|
(def mixed [[1 2] [3 4 5]])
|
||||||
|
; Inferenz: stVector<stTuple>
|
||||||
|
; Keine Matrix-Optimierung möglich, da Unter-Tupel Längen 2 und 3 haben.
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### D. Records als Tupel-Sicht
|
||||||
|
|
||||||
|
```script
|
||||||
|
(def rec {:x 1, :y 0.3})
|
||||||
|
; Physisch: stTuple<stOrdinal, stFloat>
|
||||||
|
; Logisch: Record-Mapping {:x -> 0, :y -> 1}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Nächste Schritte
|
||||||
|
|
||||||
|
1. **`Myc.Ast.Types.pas`**:
|
||||||
|
* Erweitern von `TStaticTypeKind` und `IStaticType`.
|
||||||
|
* Implementierung von `TTupleType`, `TVectorType` und `TMatrixType`.
|
||||||
|
* Implementierung der rekursiven `IsScalarPure`-Prüfung.
|
||||||
|
|
||||||
|
|
||||||
|
2. **`Myc.Ast.Compiler.TypeChecker.pas`**:
|
||||||
|
* Implementierung der Promotion-Kaskade (`stTuple` -> `stVector` -> `stMatrix`).
|
||||||
|
* Validierung der "Rechteckigkeit" bei geschachtelten Literalen.
|
||||||
|
|
||||||
|
|
||||||
|
3. **`Myc.Ast.Nodes.pas`**:
|
||||||
|
* Konsolidierung von `IArgumentList`, `IParameterList` etc. zu einer allgemeinen Tupel-Repräsentation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Soll ich nun mit der Implementierung der Typ-Erweiterungen in `Myc.Ast.Types.pas` beginnen?
|
||||||
@@ -629,7 +629,7 @@ begin
|
|||||||
idx := baseType.Definition.IndexOf(M.Member.Value);
|
idx := baseType.Definition.IndexOf(M.Member.Value);
|
||||||
if idx >= 0 then
|
if idx >= 0 then
|
||||||
begin
|
begin
|
||||||
var fieldType := TTypes.FromScalarKind(baseType.Definition.Items[idx].Value);
|
var fieldType := TTypes.FromScalarKind(baseType.Definition[idx]);
|
||||||
if baseType.Kind = stRecordSeries then
|
if baseType.Kind = stRecordSeries then
|
||||||
resType := TTypes.CreateSeries(fieldType)
|
resType := TTypes.CreateSeries(fieldType)
|
||||||
else
|
else
|
||||||
@@ -819,7 +819,7 @@ begin
|
|||||||
var def := streamType.Definition;
|
var def := streamType.Definition;
|
||||||
var idx := def.IndexOf(sel.Value);
|
var idx := def.IndexOf(sel.Value);
|
||||||
if idx >= 0 then
|
if idx >= 0 then
|
||||||
inferredType := TTypes.FromScalarKind(def.Items[idx].Value);
|
inferredType := TTypes.FromScalarKind(def[idx]);
|
||||||
end
|
end
|
||||||
else if streamType.Kind = stSeries then
|
else if streamType.Kind = stSeries then
|
||||||
begin
|
begin
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ begin
|
|||||||
SetLength(vals, rs.Def.Count);
|
SetLength(vals, rs.Def.Count);
|
||||||
var i64 := idx.AsScalar.Value.AsInt64;
|
var i64 := idx.AsScalar.Value.AsInt64;
|
||||||
for var k := 0 to rs.Def.Count - 1 do
|
for var k := 0 to rs.Def.Count - 1 do
|
||||||
vals[k] := rs.Fields[rs.Def.Items[k].Key].Items[Integer(i64)].Value;
|
vals[k] := rs.Fields[rs.Def.Keywords[k]].Items[Integer(i64)].Value;
|
||||||
Result := TDataValue.FromScalarRecord(TScalarRecord.Create(rs.Def, vals));
|
Result := TDataValue.FromScalarRecord(TScalarRecord.Create(rs.Def, vals));
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -360,7 +360,7 @@ begin
|
|||||||
case base.Kind of
|
case base.Kind of
|
||||||
vkRecordSeries: Result := TDataValue.FromSeries(base.AsRecordSeries.Fields[N.Member.Value]);
|
vkRecordSeries: Result := TDataValue.FromSeries(base.AsRecordSeries.Fields[N.Member.Value]);
|
||||||
vkScalarRecord: Result := TDataValue(base.AsScalarRecord.Fields[N.Member.Value]);
|
vkScalarRecord: Result := TDataValue(base.AsScalarRecord.Fields[N.Member.Value]);
|
||||||
vkGenericRecord: Result := TDataValue(base.AsGenericRecord.Fields[N.Member.Value]);
|
vkRecord: Result := TDataValue(base.AsRecord.Fields[N.Member.Value]);
|
||||||
vkStream: Result := TDataValue.FromSeries(base.AsStream.Series.Fields[N.Member.Value]);
|
vkStream: Result := TDataValue.FromSeries(base.AsStream.Series.Fields[N.Member.Value]);
|
||||||
else
|
else
|
||||||
raise EEvaluatorException.Create('Member error');
|
raise EEvaluatorException.Create('Member error');
|
||||||
@@ -442,7 +442,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
var key := selectors[k].Value;
|
var key := selectors[k].Value;
|
||||||
var idx := srcDef.IndexOf(key);
|
var idx := srcDef.IndexOf(key);
|
||||||
config[i][k] := TScalarRecordField.Create(key, srcDef.Items[idx].Value);
|
config[i][k] := TScalarRecordField.Create(key, srcDef[idx]);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
lambdaFunc := Visit(N.Transformation).AsMethod();
|
lambdaFunc := Visit(N.Transformation).AsMethod();
|
||||||
@@ -461,8 +461,8 @@ begin
|
|||||||
if res.IsVoid then
|
if res.IsVoid then
|
||||||
exit(False);
|
exit(False);
|
||||||
var rec := res.AsScalarRecord;
|
var rec := res.AsScalarRecord;
|
||||||
for k := 0 to rec.Def.Count - 1 do
|
for k := 0 to rec.Count - 1 do
|
||||||
R[k] := rec.Items[k].Value.Value;
|
R[k] := rec.Items[k].Value;
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
Result := TDataValue.FromStream(TPipeStream.Create(config, outputDef, sources, pipeAdapter));
|
Result := TDataValue.FromStream(TPipeStream.Create(config, outputDef, sources, pipeAdapter));
|
||||||
|
|||||||
+10
-19
@@ -646,7 +646,7 @@ begin
|
|||||||
|
|
||||||
for i := 0 to Self.FDefinition.Count - 1 do
|
for i := 0 to Self.FDefinition.Count - 1 do
|
||||||
begin
|
begin
|
||||||
if (Self.FDefinition[i].Key <> otherDef[i].Key) or (Self.FDefinition[i].Value <> otherDef[i].Value) then
|
if (Self.FDefinition.Keywords[i] <> otherDef.Keywords[i]) or (Self.FDefinition[i] <> otherDef[i]) then
|
||||||
exit(False);
|
exit(False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -656,7 +656,6 @@ end;
|
|||||||
function TRecordType.GetHashCode: Integer;
|
function TRecordType.GetHashCode: Integer;
|
||||||
var
|
var
|
||||||
i, h: Integer;
|
i, h: Integer;
|
||||||
field: TPair<IKeyword, TScalar.TKind>;
|
|
||||||
begin
|
begin
|
||||||
// Seed with Kind
|
// Seed with Kind
|
||||||
Result := THashBobJenkins.GetHashValue(FKind, SizeOf(TStaticTypeKind), 0);
|
Result := THashBobJenkins.GetHashValue(FKind, SizeOf(TStaticTypeKind), 0);
|
||||||
@@ -665,15 +664,13 @@ begin
|
|||||||
begin
|
begin
|
||||||
for i := 0 to FDefinition.Count - 1 do
|
for i := 0 to FDefinition.Count - 1 do
|
||||||
begin
|
begin
|
||||||
field := FDefinition[i];
|
|
||||||
|
|
||||||
// Hash Key
|
// Hash Key
|
||||||
// Keyword identities are unique by pointer, but let's hash their internal ID/Index for safety
|
// Keyword identities are unique by pointer, but let's hash their internal ID/Index for safety
|
||||||
h := field.Key.Idx;
|
h := FDefinition.Keywords[i].Idx;
|
||||||
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
||||||
|
|
||||||
// Hash Field Type Kind
|
// Hash Field Type Kind
|
||||||
h := Ord(field.Value);
|
h := Ord(FDefinition[i]);
|
||||||
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -682,15 +679,13 @@ end;
|
|||||||
function TRecordType.ToString: string;
|
function TRecordType.ToString: string;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
field: TPair<IKeyword, TScalar.TKind>;
|
|
||||||
begin
|
begin
|
||||||
Result := GetKind.ToString + '{';
|
Result := GetKind.ToString + '{';
|
||||||
if Assigned(FDefinition) then
|
if Assigned(FDefinition) then
|
||||||
begin
|
begin
|
||||||
for i := 0 to FDefinition.Count - 1 do
|
for i := 0 to FDefinition.Count - 1 do
|
||||||
begin
|
begin
|
||||||
field := FDefinition[i];
|
Result := Result + FDefinition.Keywords[i].Name + ': ' + FDefinition[i].ToString;
|
||||||
Result := Result + field.Key.Name + ': ' + field.Value.ToString;
|
|
||||||
if i < FDefinition.Count - 1 then
|
if i < FDefinition.Count - 1 then
|
||||||
Result := Result + ', ';
|
Result := Result + ', ';
|
||||||
end;
|
end;
|
||||||
@@ -746,7 +741,7 @@ begin
|
|||||||
for i := 0 to Self.FDefinition.Count - 1 do
|
for i := 0 to Self.FDefinition.Count - 1 do
|
||||||
begin
|
begin
|
||||||
// Deep Check of Field Types
|
// Deep Check of Field Types
|
||||||
if (Self.FDefinition[i].Key <> otherDef[i].Key) or (not Self.FDefinition[i].Value.IsEqual(otherDef[i].Value)) then
|
if (Self.FDefinition.Keywords[i] <> otherDef.Keywords[i]) or (not Self.FDefinition[i].IsEqual(otherDef[i])) then
|
||||||
exit(False);
|
exit(False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -756,7 +751,6 @@ end;
|
|||||||
function TGenericRecordType.GetHashCode: Integer;
|
function TGenericRecordType.GetHashCode: Integer;
|
||||||
var
|
var
|
||||||
i, h: Integer;
|
i, h: Integer;
|
||||||
field: TPair<IKeyword, IStaticType>;
|
|
||||||
begin
|
begin
|
||||||
h := Ord(stGenericRecord);
|
h := Ord(stGenericRecord);
|
||||||
Result := THashBobJenkins.GetHashValue(h, SizeOf(TStaticTypeKind), 0);
|
Result := THashBobJenkins.GetHashValue(h, SizeOf(TStaticTypeKind), 0);
|
||||||
@@ -765,16 +759,15 @@ begin
|
|||||||
begin
|
begin
|
||||||
for i := 0 to FDefinition.Count - 1 do
|
for i := 0 to FDefinition.Count - 1 do
|
||||||
begin
|
begin
|
||||||
field := FDefinition[i];
|
|
||||||
|
|
||||||
// Hash Key (using Idx)
|
// Hash Key (using Idx)
|
||||||
h := field.Key.Idx;
|
h := FDefinition.Keywords[i].Idx;
|
||||||
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
||||||
|
|
||||||
// Hash Value Type
|
// Hash Value Type
|
||||||
if Assigned(field.Value) then
|
var val := FDefinition[i];
|
||||||
|
if Assigned(val) then
|
||||||
begin
|
begin
|
||||||
h := field.Value.GetHashCode;
|
h := val.GetHashCode;
|
||||||
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -784,15 +777,13 @@ end;
|
|||||||
function TGenericRecordType.ToString: string;
|
function TGenericRecordType.ToString: string;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
field: TPair<IKeyword, IStaticType>;
|
|
||||||
begin
|
begin
|
||||||
Result := GetKind.ToString + '{';
|
Result := GetKind.ToString + '{';
|
||||||
if Assigned(FDefinition) then
|
if Assigned(FDefinition) then
|
||||||
begin
|
begin
|
||||||
for i := 0 to FDefinition.Count - 1 do
|
for i := 0 to FDefinition.Count - 1 do
|
||||||
begin
|
begin
|
||||||
field := FDefinition[i];
|
Result := Result + FDefinition.Keywords[i].Name + ': ' + FDefinition[i].ToString;
|
||||||
Result := Result + field.Key.Name + ': ' + field.Value.ToString;
|
|
||||||
if i < FDefinition.Count - 1 then
|
if i < FDefinition.Count - 1 then
|
||||||
Result := Result + ', ';
|
Result := Result + ', ';
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ uses
|
|||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
System.Generics.Defaults,
|
System.Generics.Defaults,
|
||||||
System.SyncObjs;
|
System.SyncObjs,
|
||||||
|
Myc.Data.Tuple;
|
||||||
|
|
||||||
type
|
type
|
||||||
// The runtime representation of an interned keyword
|
// The runtime representation of an interned keyword
|
||||||
@@ -49,17 +50,18 @@ type
|
|||||||
// Defines a mapping from Keywords to a generic value T
|
// Defines a mapping from Keywords to a generic value T
|
||||||
IKeywordMapping<T> = interface
|
IKeywordMapping<T> = interface
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetItems(Idx: Integer): TPair<IKeyword, T>;
|
function GetItems(Idx: Integer): T;
|
||||||
function GetFields(const Key: IKeyword): T;
|
function GetFields(const Key: IKeyword): T;
|
||||||
function GetCount: Integer;
|
function GetCount: Integer;
|
||||||
|
function GetKeywords(Idx: Integer): IKeyword;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
// Finds the 0-based index for a given keyword. Returns -1 if not found.
|
// Finds the 0-based index for a given keyword. Returns -1 if not found.
|
||||||
function IndexOf(const Key: IKeyword): Integer;
|
function IndexOf(const Key: IKeyword): Integer;
|
||||||
|
|
||||||
// Access value by Keyword (raises exception if not found)
|
property Keywords[Idx: Integer]: IKeyword read GetKeywords;
|
||||||
property Fields[const Key: IKeyword]: T read GetFields;
|
property Fields[const Key: IKeyword]: T read GetFields;
|
||||||
// Access Key-Value pair by Index (0..Count-1)
|
property Items[Idx: Integer]: T read GetItems; default;
|
||||||
property Items[Idx: Integer]: TPair<IKeyword, T> read GetItems; default;
|
|
||||||
property Count: Integer read GetCount;
|
property Count: Integer read GetCount;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -74,8 +76,9 @@ type
|
|||||||
FFields: TArray<TPair<IKeyword, T>>;
|
FFields: TArray<TPair<IKeyword, T>>;
|
||||||
FFirst, FLast: Integer;
|
FFirst, FLast: Integer;
|
||||||
|
|
||||||
function GetItems(Idx: Integer): TPair<IKeyword, T>;
|
function GetItems(Idx: Integer): T;
|
||||||
function GetFields(const Key: IKeyword): T;
|
function GetFields(const Key: IKeyword): T;
|
||||||
|
function GetKeywords(Idx: Integer): IKeyword;
|
||||||
function GetCount: Integer;
|
function GetCount: Integer;
|
||||||
public
|
public
|
||||||
// Expects AFields to be in the desired (canonical) order
|
// Expects AFields to be in the desired (canonical) order
|
||||||
@@ -100,7 +103,8 @@ type
|
|||||||
FFields: TArray<TPair<IKeyword, T>>;
|
FFields: TArray<TPair<IKeyword, T>>;
|
||||||
|
|
||||||
// IKeywordMapping implementation
|
// IKeywordMapping implementation
|
||||||
function GetItems(Idx: Integer): TPair<IKeyword, T>;
|
function GetItems(Idx: Integer): T;
|
||||||
|
function GetKeywords(Idx: Integer): IKeyword;
|
||||||
function GetFields(const Key: IKeyword): T;
|
function GetFields(const Key: IKeyword): T;
|
||||||
function GetCount: Integer;
|
function GetCount: Integer;
|
||||||
public
|
public
|
||||||
@@ -224,9 +228,9 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Interface method for Items property
|
// Interface method for Items property
|
||||||
function TKeywordMappingRegistry<T>.TKeywordMapping.GetItems(Idx: Integer): TPair<IKeyword, T>;
|
function TKeywordMappingRegistry<T>.TKeywordMapping.GetItems(Idx: Integer): T;
|
||||||
begin
|
begin
|
||||||
Result := FFields[Idx];
|
Result := FFields[Idx].Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Interface method for Fields property
|
// Interface method for Fields property
|
||||||
@@ -240,6 +244,11 @@ begin
|
|||||||
Result := FFields[idx].Value;
|
Result := FFields[idx].Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TKeywordMappingRegistry<T>.TKeywordMapping.GetKeywords(Idx: Integer): IKeyword;
|
||||||
|
begin
|
||||||
|
Result := FFields[Idx].Key;
|
||||||
|
end;
|
||||||
|
|
||||||
function TKeywordMappingRegistry<T>.TKeywordMapping.IndexOf(const Key: IKeyword): Integer;
|
function TKeywordMappingRegistry<T>.TKeywordMapping.IndexOf(const Key: IKeyword): Integer;
|
||||||
var
|
var
|
||||||
keyIdx, mapIdx: Integer;
|
keyIdx, mapIdx: Integer;
|
||||||
@@ -312,9 +321,14 @@ begin
|
|||||||
Result := FFields[idx].Value;
|
Result := FFields[idx].Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TKeywordMapping<T>.GetItems(Idx: Integer): TPair<IKeyword, T>;
|
function TKeywordMapping<T>.GetItems(Idx: Integer): T;
|
||||||
begin
|
begin
|
||||||
Result := FFields[Idx];
|
Result := FFields[Idx].Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TKeywordMapping<T>.GetKeywords(Idx: Integer): IKeyword;
|
||||||
|
begin
|
||||||
|
Result := FFields[Idx].Key;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TKeywordMapping<T>.IndexOf(const Key: IKeyword): Integer;
|
function TKeywordMapping<T>.IndexOf(const Key: IKeyword): Integer;
|
||||||
|
|||||||
@@ -136,14 +136,7 @@ type
|
|||||||
TScalarRecordField = TPair<IKeyword, TScalar.TKind>;
|
TScalarRecordField = TPair<IKeyword, TScalar.TKind>;
|
||||||
IScalarRecordDefinition = IKeywordMapping<TScalar.TKind>;
|
IScalarRecordDefinition = IKeywordMapping<TScalar.TKind>;
|
||||||
|
|
||||||
IScalarRecord = interface(IKeywordMapping<TScalar>)
|
TScalarRecord = class(TInterfacedObject, IKeywordMapping<TScalar>)
|
||||||
{$region 'private'}
|
|
||||||
function GetDef: IScalarRecordDefinition;
|
|
||||||
{$endregion}
|
|
||||||
property Def: IScalarRecordDefinition read GetDef;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TScalarRecord = class(TInterfacedObject, IKeywordMapping<TScalar>, IScalarRecord)
|
|
||||||
type
|
type
|
||||||
TRegistry = TKeywordMappingRegistry<TScalar.TKind>;
|
TRegistry = TKeywordMappingRegistry<TScalar.TKind>;
|
||||||
private
|
private
|
||||||
@@ -152,12 +145,11 @@ type
|
|||||||
|
|
||||||
function IndexOf(const Key: IKeyword): Integer;
|
function IndexOf(const Key: IKeyword): Integer;
|
||||||
function GetCount: Integer;
|
function GetCount: Integer;
|
||||||
function GetDef: IScalarRecordDefinition;
|
function GetItems(Idx: Integer): TScalar;
|
||||||
function GetItems(Idx: Integer): TPair<IKeyword, TScalar>;
|
|
||||||
function GetFields(const Key: IKeyword): TScalar;
|
function GetFields(const Key: IKeyword): TScalar;
|
||||||
|
function GetKeywords(Idx: Integer): IKeyword;
|
||||||
public
|
public
|
||||||
constructor Create(const ADef: IScalarRecordDefinition; const AData: TArray<TScalar.TValue>);
|
constructor Create(const ADef: IScalarRecordDefinition; const AData: TArray<TScalar.TValue>);
|
||||||
property Def: IScalarRecordDefinition read GetDef;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ISeries = interface
|
ISeries = interface
|
||||||
@@ -228,7 +220,8 @@ type
|
|||||||
FFields: TArray<TMemberSeries>;
|
FFields: TArray<TMemberSeries>;
|
||||||
FTotalCount: Int64;
|
FTotalCount: Int64;
|
||||||
|
|
||||||
function GetItems(Idx: Integer): TPair<IKeyword, ISeries>;
|
function GetItems(Idx: Integer): ISeries;
|
||||||
|
function GetKeywords(Idx: Integer): IKeyword;
|
||||||
function GetCount: Integer;
|
function GetCount: Integer;
|
||||||
function IndexOf(const Key: IKeyword): Integer;
|
function IndexOf(const Key: IKeyword): Integer;
|
||||||
|
|
||||||
@@ -824,8 +817,8 @@ begin
|
|||||||
var lb := FDef.Count * Integer(Lookback);
|
var lb := FDef.Count * Integer(Lookback);
|
||||||
for var i := 0 to FDef.Count - 1 do
|
for var i := 0 to FDef.Count - 1 do
|
||||||
begin
|
begin
|
||||||
Assert(Item[i].Key = FDef[i].Key, 'Mapping does not fit series definition');
|
Assert(Item.Keywords[i] = FDef.Keywords[i], 'Mapping does not fit series definition');
|
||||||
FArray.Add(Item[i].Value.Value, lb);
|
FArray.Add(Item[i].Value, lb);
|
||||||
end;
|
end;
|
||||||
inc(FTotalCount);
|
inc(FTotalCount);
|
||||||
end;
|
end;
|
||||||
@@ -875,10 +868,14 @@ begin
|
|||||||
Result := Length(FFields);
|
Result := Length(FFields);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TScalarRecordSeries.GetItems(Idx: Integer): TPair<IKeyword, ISeries>;
|
function TScalarRecordSeries.GetItems(Idx: Integer): ISeries;
|
||||||
begin
|
begin
|
||||||
Result.Key := FDef.Items[Idx].Key;
|
Result := FFields[Idx];
|
||||||
Result.Value := FFields[Idx];
|
end;
|
||||||
|
|
||||||
|
function TScalarRecordSeries.GetKeywords(Idx: Integer): IKeyword;
|
||||||
|
begin
|
||||||
|
Result := FDef.Keywords[Idx];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TScalarRecordSeries.IndexOf(const Key: IKeyword): Integer;
|
function TScalarRecordSeries.IndexOf(const Key: IKeyword): Integer;
|
||||||
@@ -946,7 +943,7 @@ end;
|
|||||||
constructor TScalarRecordSeries.TMemberSeries.Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
|
constructor TScalarRecordSeries.TMemberSeries.Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
|
||||||
begin
|
begin
|
||||||
inherited Create(ARecordSeries);
|
inherited Create(ARecordSeries);
|
||||||
FKind := ARecordSeries.FDef[AElementIdx].Value;
|
FKind := ARecordSeries.FDef[AElementIdx];
|
||||||
FOffset := sizeof(TScalar.TValue) * AElementIdx;
|
FOffset := sizeof(TScalar.TValue) * AElementIdx;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -1043,11 +1040,6 @@ begin
|
|||||||
Result := FDef.Count;
|
Result := FDef.Count;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TScalarRecord.GetDef: IScalarRecordDefinition;
|
|
||||||
begin
|
|
||||||
Result := FDef;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TScalarRecord.GetFields(const Key: IKeyword): TScalar;
|
function TScalarRecord.GetFields(const Key: IKeyword): TScalar;
|
||||||
var
|
var
|
||||||
idx: Integer;
|
idx: Integer;
|
||||||
@@ -1057,15 +1049,19 @@ begin
|
|||||||
exit(Default(TScalar));
|
exit(Default(TScalar));
|
||||||
|
|
||||||
// Return the raw TValue at the offset
|
// Return the raw TValue at the offset
|
||||||
Result.Kind := FDef[idx].Value;
|
Result.Kind := FDef[idx];
|
||||||
Result.Value := FData[idx];
|
Result.Value := FData[idx];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TScalarRecord.GetItems(Idx: Integer): TPair<IKeyword, TScalar>;
|
function TScalarRecord.GetItems(Idx: Integer): TScalar;
|
||||||
begin
|
begin
|
||||||
Result.Key := FDef[Idx].Key;
|
Result.Kind := FDef[idx];
|
||||||
Result.Value.Kind := FDef[idx].Value;
|
Result.Value := FData[idx];
|
||||||
Result.Value.Value := FData[idx];
|
end;
|
||||||
|
|
||||||
|
function TScalarRecord.GetKeywords(Idx: Integer): IKeyword;
|
||||||
|
begin
|
||||||
|
Result := FDef.Keywords[Idx];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TScalarRecord.IndexOf(const Key: IKeyword): Integer;
|
function TScalarRecord.IndexOf(const Key: IKeyword): Integer;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
unit Myc.Data.Tuple;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
type
|
||||||
|
ITuple<T> = interface
|
||||||
|
{$region 'private'}
|
||||||
|
function GetItems(Idx: Integer): T;
|
||||||
|
function GetCount: Integer;
|
||||||
|
{$endregion}
|
||||||
|
property Items[Idx: Integer]: T read GetItems; default;
|
||||||
|
property Count: Integer read GetCount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
end.
|
||||||
+29
-24
@@ -14,23 +14,31 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
TDataValueKind = (
|
TDataValueKind = (
|
||||||
|
// base types
|
||||||
vkVoid,
|
vkVoid,
|
||||||
vkScalar,
|
vkScalar,
|
||||||
vkText,
|
vkText,
|
||||||
|
vkMethod,
|
||||||
|
// generic structured types
|
||||||
|
vkTuple,
|
||||||
|
vkRecord,
|
||||||
|
// scalar structured types
|
||||||
|
// vkScalarTuple, (later)
|
||||||
|
vkScalarRecord,
|
||||||
|
// Series and streams
|
||||||
vkSeries,
|
vkSeries,
|
||||||
vkRecordSeries,
|
vkRecordSeries,
|
||||||
vkScalarRecord,
|
vkStream,
|
||||||
vkGenericRecord,
|
// internal types
|
||||||
vkManagedObject,
|
vkManagedObject,
|
||||||
vkMethod,
|
|
||||||
vkInterface,
|
vkInterface,
|
||||||
vkPointer,
|
vkPointer,
|
||||||
vkStream,
|
|
||||||
vkGeneric
|
vkGeneric
|
||||||
);
|
);
|
||||||
|
|
||||||
TDataValue = record
|
TDataValue = record
|
||||||
type
|
type
|
||||||
|
TTuple = TArray<TDataValue>;
|
||||||
TFunc = reference to function(const ArgNodes: TArray<TDataValue>): TDataValue;
|
TFunc = reference to function(const ArgNodes: TArray<TDataValue>): TDataValue;
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -84,7 +92,7 @@ type
|
|||||||
class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static;
|
class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static;
|
||||||
|
|
||||||
class function FromRecordSeries(const AValue: IWriteableScalarRecordSeries): TDataValue; static; inline;
|
class function FromRecordSeries(const AValue: IWriteableScalarRecordSeries): TDataValue; static; inline;
|
||||||
class function FromScalarRecord(const AValue: IScalarRecord): TDataValue; static; inline;
|
class function FromScalarRecord(const AValue: IKeywordMapping<TScalar>): TDataValue; static; inline;
|
||||||
class function FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue; static; inline;
|
class function FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue; static; inline;
|
||||||
class function FromSeries(const AValue: ISeries): TDataValue; static; inline;
|
class function FromSeries(const AValue: ISeries): TDataValue; static; inline;
|
||||||
class function FromStream(const AStream: IStream): TDataValue; static; inline;
|
class function FromStream(const AStream: IStream): TDataValue; static; inline;
|
||||||
@@ -94,8 +102,8 @@ type
|
|||||||
function AsMethod: TFunc; inline;
|
function AsMethod: TFunc; inline;
|
||||||
function AsText: String; inline;
|
function AsText: String; inline;
|
||||||
function AsRecordSeries: IWriteableScalarRecordSeries; inline;
|
function AsRecordSeries: IWriteableScalarRecordSeries; inline;
|
||||||
function AsScalarRecord: IScalarRecord; inline;
|
function AsScalarRecord: IKeywordMapping<TScalar>; inline;
|
||||||
function AsGenericRecord: IKeywordMapping<TDataValue>; inline;
|
function AsRecord: IKeywordMapping<TDataValue>; inline;
|
||||||
function AsSeries: ISeries; inline;
|
function AsSeries: ISeries; inline;
|
||||||
function AsObject: TObject; overload; inline;
|
function AsObject: TObject; overload; inline;
|
||||||
|
|
||||||
@@ -141,7 +149,7 @@ begin
|
|||||||
vkSeries: Result := 'Series';
|
vkSeries: Result := 'Series';
|
||||||
vkRecordSeries: Result := 'RecordSeries';
|
vkRecordSeries: Result := 'RecordSeries';
|
||||||
vkScalarRecord: Result := 'ScalarRecord';
|
vkScalarRecord: Result := 'ScalarRecord';
|
||||||
vkGenericRecord: Result := 'GenericRecord';
|
vkRecord: Result := 'Record';
|
||||||
vkManagedObject: Result := 'Object';
|
vkManagedObject: Result := 'Object';
|
||||||
vkMethod: Result := 'Method';
|
vkMethod: Result := 'Method';
|
||||||
vkInterface: Result := 'Interface';
|
vkInterface: Result := 'Interface';
|
||||||
@@ -268,7 +276,7 @@ begin
|
|||||||
Result.FInterface := AValue;
|
Result.FInterface := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TDataValue.FromScalarRecord(const AValue: IScalarRecord): TDataValue;
|
class function TDataValue.FromScalarRecord(const AValue: IKeywordMapping<TScalar>): TDataValue;
|
||||||
begin
|
begin
|
||||||
Result.FKind := vkScalarRecord;
|
Result.FKind := vkScalarRecord;
|
||||||
Result.FInterface := AValue;
|
Result.FInterface := AValue;
|
||||||
@@ -276,7 +284,7 @@ end;
|
|||||||
|
|
||||||
class function TDataValue.FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue;
|
class function TDataValue.FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue;
|
||||||
begin
|
begin
|
||||||
Result.FKind := vkGenericRecord;
|
Result.FKind := vkRecord;
|
||||||
Result.FInterface := AValue;
|
Result.FInterface := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -350,17 +358,17 @@ begin
|
|||||||
Result := Pointer(FScalar.Value.AsInt64);
|
Result := Pointer(FScalar.Value.AsInt64);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDataValue.AsScalarRecord: IScalarRecord;
|
function TDataValue.AsScalarRecord: IKeywordMapping<TScalar>;
|
||||||
begin
|
begin
|
||||||
if (FKind <> vkScalarRecord) then
|
if (FKind <> vkScalarRecord) then
|
||||||
raise EInvalidCast.Create('Cannot read value as Record.');
|
raise EInvalidCast.Create('Cannot read value as Record.');
|
||||||
Result := IScalarRecord(FInterface);
|
Result := IKeywordMapping<TScalar>(FInterface);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDataValue.AsGenericRecord: IKeywordMapping<TDataValue>;
|
function TDataValue.AsRecord: IKeywordMapping<TDataValue>;
|
||||||
begin
|
begin
|
||||||
if (FKind <> vkGenericRecord) then
|
if (FKind <> vkRecord) then
|
||||||
raise EInvalidCast.Create('Cannot read value as GenericRecord.');
|
raise EInvalidCast.Create('Cannot read value as Record.');
|
||||||
Result := IKeywordMapping<TDataValue>(FInterface);
|
Result := IKeywordMapping<TDataValue>(FInterface);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -388,7 +396,6 @@ end;
|
|||||||
function TDataValue.ToString: String;
|
function TDataValue.ToString: String;
|
||||||
var
|
var
|
||||||
sb: TStringBuilder;
|
sb: TStringBuilder;
|
||||||
field: TScalarRecordField;
|
|
||||||
genField: TPair<IKeyword, TDataValue>;
|
genField: TPair<IKeyword, TDataValue>;
|
||||||
first: Boolean;
|
first: Boolean;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
@@ -412,8 +419,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
if not first then
|
if not first then
|
||||||
sb.Append(',');
|
sb.Append(',');
|
||||||
field := series.Def[i];
|
sb.Append(series.Def.Keywords[i].Name);
|
||||||
sb.Append(field.Key.Name);
|
|
||||||
first := False;
|
first := False;
|
||||||
end;
|
end;
|
||||||
sb.Append('}');
|
sb.Append('}');
|
||||||
@@ -433,7 +439,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
if not first then
|
if not first then
|
||||||
sb.Append(',');
|
sb.Append(',');
|
||||||
sb.Append(rec[i].Key.Name);
|
sb.Append(rec.Keywords[i].Name);
|
||||||
first := False;
|
first := False;
|
||||||
end;
|
end;
|
||||||
sb.Append('}');
|
sb.Append('}');
|
||||||
@@ -442,9 +448,9 @@ begin
|
|||||||
sb.Free;
|
sb.Free;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
vkGenericRecord:
|
vkRecord:
|
||||||
begin
|
begin
|
||||||
var rec := AsGenericRecord;
|
var rec := AsRecord;
|
||||||
sb := TStringBuilder.Create;
|
sb := TStringBuilder.Create;
|
||||||
try
|
try
|
||||||
sb.Append('{');
|
sb.Append('{');
|
||||||
@@ -453,12 +459,11 @@ begin
|
|||||||
begin
|
begin
|
||||||
if not first then
|
if not first then
|
||||||
sb.Append(',');
|
sb.Append(',');
|
||||||
genField := rec[i];
|
sb.Append(rec.Keywords[i].Name);
|
||||||
sb.Append(genField.Key.Name);
|
|
||||||
first := False;
|
first := False;
|
||||||
end;
|
end;
|
||||||
sb.Append('}');
|
sb.Append('}');
|
||||||
Result := Format('<generic_record%s>', [sb.ToString]);
|
Result := Format('<record%s>', [sb.ToString]);
|
||||||
finally
|
finally
|
||||||
sb.Free;
|
sb.Free;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ begin
|
|||||||
idx := genRec.IndexOf(Myc.Data.Keyword.TKeywordRegistry.Intern('Add'));
|
idx := genRec.IndexOf(Myc.Data.Keyword.TKeywordRegistry.Intern('Add'));
|
||||||
Assert.IsTrue(idx >= 0, 'Method Add should exist in type definition');
|
Assert.IsTrue(idx >= 0, 'Method Add should exist in type definition');
|
||||||
|
|
||||||
methodType := genRec.Items[idx].Value;
|
methodType := genRec.Items[idx];
|
||||||
Assert.AreEqual(TStaticTypeKind.stMethod, methodType.Kind);
|
Assert.AreEqual(TStaticTypeKind.stMethod, methodType.Kind);
|
||||||
// Add has 2 args (Ordinal, Ordinal) -> Ordinal
|
// Add has 2 args (Ordinal, Ordinal) -> Ordinal
|
||||||
Assert.AreEqual(Int64(2), Length(methodType.Signatures[0].ParamTypes));
|
Assert.AreEqual(Int64(2), Length(methodType.Signatures[0].ParamTypes));
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ begin
|
|||||||
SetLength(RowData, Def.Count);
|
SetLength(RowData, Def.Count);
|
||||||
for i := 0 to Def.Count - 1 do
|
for i := 0 to Def.Count - 1 do
|
||||||
begin
|
begin
|
||||||
Key := Def.Items[i].Key;
|
Key := Def.Keywords[i];
|
||||||
if Key = kTime then
|
if Key = kTime then
|
||||||
RowData[i] := FLastTime
|
RowData[i] := FLastTime
|
||||||
else if Key = kOpen then
|
else if Key = kOpen then
|
||||||
|
|||||||
+2
-1
@@ -37,7 +37,8 @@ uses
|
|||||||
Test.Myc.Ast.NullPropagation in 'AST\Test.Myc.Ast.NullPropagation.pas',
|
Test.Myc.Ast.NullPropagation in 'AST\Test.Myc.Ast.NullPropagation.pas',
|
||||||
Test.Myc.Ast.Pipes in 'AST\Test.Myc.Ast.Pipes.pas',
|
Test.Myc.Ast.Pipes in 'AST\Test.Myc.Ast.Pipes.pas',
|
||||||
Test.Myc.Ast.Integration in 'Test.Myc.Ast.Integration.pas',
|
Test.Myc.Ast.Integration in 'Test.Myc.Ast.Integration.pas',
|
||||||
Demo.Finance in 'Demo.Finance.pas';
|
Demo.Finance in 'Demo.Finance.pas',
|
||||||
|
Myc.Data.Tuple in '..\Src\Data\Myc.Data.Tuple.pas';
|
||||||
|
|
||||||
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
|
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
|
||||||
{$IFNDEF TESTINSIGHT}
|
{$IFNDEF TESTINSIGHT}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ $(PreBuildEvent)]]></PreBuildEvent>
|
|||||||
<DCCReference Include="AST\Test.Myc.Ast.Pipes.pas"/>
|
<DCCReference Include="AST\Test.Myc.Ast.Pipes.pas"/>
|
||||||
<DCCReference Include="Test.Myc.Ast.Integration.pas"/>
|
<DCCReference Include="Test.Myc.Ast.Integration.pas"/>
|
||||||
<DCCReference Include="Demo.Finance.pas"/>
|
<DCCReference Include="Demo.Finance.pas"/>
|
||||||
|
<DCCReference Include="..\Src\Data\Myc.Data.Tuple.pas"/>
|
||||||
<BuildConfiguration Include="Base">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
|||||||
Reference in New Issue
Block a user