Ast development

This commit is contained in:
Michael Schimmel
2025-09-02 17:49:30 +02:00
parent a83bbf4f54
commit eb7902d9e8
7 changed files with 257 additions and 83 deletions
+36 -37
View File
@@ -399,9 +399,8 @@ var
series: TScalarRecordSeries; series: TScalarRecordSeries;
recordValue: TScalarRecord; recordValue: TScalarRecord;
visitor: IAstVisitor; visitor: IAstVisitor;
ast: IAstNode; ast: IExpressionNode;
resultValue: TAstValue; resultValue: TAstValue;
resultRecord: TScalarRecord;
i: Integer; i: Integer;
begin begin
Memo1.Lines.Clear; Memo1.Lines.Clear;
@@ -409,17 +408,13 @@ begin
// --- 1. Arrange (in Delphi) --- // --- 1. Arrange (in Delphi) ---
Memo1.Lines.Add('1. Arranging test data in Delphi...'); Memo1.Lines.Add('1. Arranging test data in Delphi...');
// Clear and prepare the global scope for the script.
FGScope.Clear; FGScope.Clear;
RegisterNativeFunctions(FGScope); RegisterNativeFunctions(FGScope);
// Generate JSON definition from our test record type.
jsonDef := TRttiAstHelper.RecordDefinitionToJson(TypeInfo(TOHLCV)); jsonDef := TRttiAstHelper.RecordDefinitionToJson(TypeInfo(TOHLCV));
FGScope.SetValue('ohlcvDef', TAstValue.FromText(jsonDef)); FGScope.SetValue('ohlcvDef', TAstValue.FromText(jsonDef));
Memo1.Lines.Add(' - Generated and injected JSON definition for TOHLCV.'); Memo1.Lines.Add(' - Generated and injected JSON definition for TOHLCV.');
// Create a TScalarRecordSeries and populate it with some data.
recordDef := TRttiAstHelper.JsonToRecordDefinition(jsonDef); recordDef := TRttiAstHelper.JsonToRecordDefinition(jsonDef);
series := TScalarRecordSeries.Create(recordDef); series := TScalarRecordSeries.Create(recordDef);
for i := 0 to 4 do for i := 0 to 4 do
@@ -439,7 +434,6 @@ begin
series.Add(recordValue); series.Add(recordValue);
end; end;
// Inject the pre-populated series into the script's scope.
FGScope.SetValue('ohlcvSeries', TAstValue.FromRecordSeries(series)); FGScope.SetValue('ohlcvSeries', TAstValue.FromRecordSeries(series));
Memo1.Lines.Add(Format(' - Created and injected a series with %d records.', [series.TotalCount])); Memo1.Lines.Add(Format(' - Created and injected a series with %d records.', [series.TotalCount]));
Memo1.Lines.Add(''); Memo1.Lines.Add('');
@@ -447,24 +441,32 @@ begin
// --- 2. Act (in Script) --- // --- 2. Act (in Script) ---
Memo1.Lines.Add('2. Executing script...'); Memo1.Lines.Add('2. Executing script...');
// Create an AST that: // This script now tests member access and indexing the resulting member series.
// a) Creates a new, empty series to test the native function. // let closeColumn = ohlcvSeries.Close;
// b) Accesses the 3rd element (index 2) of the pre-populated series. // let secondClose = closeColumn[1];
// c) Returns the accessed record as the final result. // secondClose
ast := ast :=
TAst.Block( TAst.LambdaExpr(
[ [],
TAst.VarDecl( TAst.Block(
TAst.Identifier('newSeries'), [
TAst.FunctionCall(TAst.Identifier('CreateRecordSeries'), [TAst.Identifier('ohlcvDef')]) TAst.VarDecl(
), TAst.Identifier('closeColumn'),
TAst.Indexer(TAst.Identifier('ohlcvSeries'), TAst.Constant(TScalar.FromInt64(2))) TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
] ),
TAst.VarDecl(
TAst.Identifier('secondClose'),
TAst.Indexer(TAst.Identifier('closeColumn'), TAst.Constant(TScalar.FromInt64(1)))
),
TAst.AssignResult(TAst.Identifier('secondClose'))
]
)
); );
FLastAst := ast; FLastAst := ast;
visitor := TEvaluatorVisitor.Create(FGScope); visitor := TEvaluatorVisitor.Create(FGScope);
resultValue := ast.Accept(visitor); resultValue := TAst.FunctionCall(ast, []).Accept(visitor);
Memo1.Lines.Add(' - Script finished.'); Memo1.Lines.Add(' - Script finished.');
Memo1.Lines.Add(Format(' - Script returned value of type: %s', [GetEnumName(TypeInfo(TAstValueKind), Ord(resultValue.Kind))])); Memo1.Lines.Add(Format(' - Script returned value of type: %s', [GetEnumName(TypeInfo(TAstValueKind), Ord(resultValue.Kind))]));
Memo1.Lines.Add(''); Memo1.Lines.Add('');
@@ -472,34 +474,31 @@ begin
// --- 3. Assert (in Delphi) --- // --- 3. Assert (in Delphi) ---
Memo1.Lines.Add('3. Asserting results in Delphi...'); Memo1.Lines.Add('3. Asserting results in Delphi...');
if (resultValue.Kind <> avkRecord) then if (resultValue.Kind <> avkScalar) then
begin begin
Memo1.Lines.Add('TEST FAILED: Script did not return a record.'); Memo1.Lines.Add('TEST FAILED: Script did not return a scalar value.');
exit; exit;
end; end;
Memo1.Lines.Add(' - Result is a scalar, as expected.');
resultRecord := resultValue.AsRecord; var resultScalar := resultValue.AsScalar;
Memo1.Lines.Add(' - Result is a record, as expected.'); if (resultScalar.Kind <> skDouble) then
begin
Memo1.Lines.Add('TEST FAILED: Returned scalar is not a Double.');
exit;
end;
Memo1.Lines.Add(' - Returned scalar is a Double, as expected.');
// Verify the 'Close' price of the 3rd record (index 2) var closeValue := resultScalar.Value.AsDouble;
var closeValue := resultRecord.Items['Close'].Value.AsDouble; // The script asks for index [1], which is the second-to-last element.
var expectedClose := 102.0 + 2; // from the data generation loop for i=2 // The data was generated for i in 0..4, so the second-to-last is i=3.
var expectedClose := 102.0 + 3;
if (abs(closeValue - expectedClose) > 0.001) then if (abs(closeValue - expectedClose) > 0.001) then
begin begin
Memo1.Lines.Add(Format('TEST FAILED: Expected Close price %f, but got %f.', [expectedClose, closeValue])); Memo1.Lines.Add(Format('TEST FAILED: Expected Close price %f, but got %f.', [expectedClose, closeValue]));
exit; exit;
end; end;
Memo1.Lines.Add(Format(' - Verified Close price: %f.', [closeValue])); Memo1.Lines.Add(Format(' - Verified Close price: %f.', [closeValue]));
// Verify the 'Volume' of the 3rd record (index 2)
var volumeValue := resultRecord.Items['Volume'].Value.AsInt64;
var expectedVolume := 10000 * (2 + 1); // for i=2
if (volumeValue <> expectedVolume) then
begin
Memo1.Lines.Add(Format('TEST FAILED: Expected Volume %d, but got %d.', [expectedVolume, volumeValue]));
exit;
end;
Memo1.Lines.Add(Format(' - Verified Volume: %d.', [volumeValue]));
Memo1.Lines.Add(''); Memo1.Lines.Add('');
Memo1.Lines.Add('--- TEST PASSED ---'); Memo1.Lines.Add('--- TEST PASSED ---');
end; end;
+47
View File
@@ -128,6 +128,7 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
function VisitAssignment(const Node: IAssignmentNode): TAstValue; function VisitAssignment(const Node: IAssignmentNode): TAstValue;
function VisitIndexer(const Node: IIndexerNode): TAstValue; function VisitIndexer(const Node: IIndexerNode): TAstValue;
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
end; end;
TAuraWorkspace = class(TStyledControl) TAuraWorkspace = class(TStyledControl)
@@ -200,6 +201,7 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
function VisitAssignment(const Node: IAssignmentNode): TAstValue; function VisitAssignment(const Node: IAssignmentNode): TAstValue;
function VisitIndexer(const Node: IIndexerNode): TAstValue; function VisitIndexer(const Node: IIndexerNode): TAstValue;
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
end; end;
constructor TAstToTextVisitor.Create; constructor TAstToTextVisitor.Create;
@@ -300,6 +302,14 @@ begin
end; end;
end; end;
function TAstToTextVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
var
baseStr: string;
begin
baseStr := Node.Base.Accept(Self).AsText;
Result := TAstValue.FromText(Format('%s.%s', [baseStr, Node.Member.Name]));
end;
function TAstToTextVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue; function TAstToTextVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
begin begin
var condStr := Node.Condition.Accept(Self).AsText; var condStr := Node.Condition.Accept(Self).AsText;
@@ -1032,6 +1042,43 @@ begin
Result := TAstValue.Void; Result := TAstValue.Void;
end; end;
function TAstToAuraNodeVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
var
details: String;
begin
// Implements the visualization for member access (e.g., series.field).
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
begin
var memberNode := CreateNodeControl('Expression', details);
FLastResult.OutputPin := CreateOutput(memberNode);
FinalizeNodeLayout(memberNode);
end
else
begin
VisitOperatorNode(
[Node.Base],
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
var
memberAccessNode: TAuraNode;
basePin, outPin: TControl;
begin
memberAccessNode := BuildNodeControl('Get ' + Node.Member.Name, '');
basePin := CreateInput(memberAccessNode, 'Base');
outPin := CreateOutput(memberAccessNode);
if Assigned(InputResults[0].OutputPin) then
FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, basePin));
FinalizeNodeLayout(memberAccessNode);
Result.LayoutNode := memberAccessNode;
Result.OutputPin := outPin;
end
);
end;
Result := TAstValue.Void;
end;
function TAstToAuraNodeVisitor.VisitOperatorNode( function TAstToAuraNodeVisitor.VisitOperatorNode(
const InputExpressions: TArray<IExpressionNode>; const InputExpressions: TArray<IExpressionNode>;
const CreateParentProc: TCreateParentNodeProc; const CreateParentProc: TCreateParentNodeProc;
+62 -11
View File
@@ -32,6 +32,7 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; virtual; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; virtual;
function VisitAssignment(const Node: IAssignmentNode): TAstValue; virtual; function VisitAssignment(const Node: IAssignmentNode): TAstValue; virtual;
function VisitIndexer(const Node: IIndexerNode): TAstValue; virtual; function VisitIndexer(const Node: IIndexerNode): TAstValue; virtual;
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue; virtual;
end; end;
// TDebugEvaluatorVisitor now overrides all visit methods for full tracing // TDebugEvaluatorVisitor now overrides all visit methods for full tracing
@@ -61,6 +62,7 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; override;
function VisitAssignment(const Node: IAssignmentNode): TAstValue; override; function VisitAssignment(const Node: IAssignmentNode): TAstValue; override;
function VisitIndexer(const Node: IIndexerNode): TAstValue; override; function VisitIndexer(const Node: IIndexerNode): TAstValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue; override;
end; end;
// Registers all native core functions in the given scope. // Registers all native core functions in the given scope.
@@ -208,7 +210,7 @@ begin
skInteger: Result := (AValue.AsScalar.Value.AsInteger <> 0); skInteger: Result := (AValue.AsScalar.Value.AsInteger <> 0);
skInt64: Result := (AValue.AsScalar.Value.AsInt64 <> 0); skInt64: Result := (AValue.AsScalar.Value.AsInt64 <> 0);
skUInt64: Result := (AValue.AsScalar.Value.AsUInt64 <> 0); skUInt64: Result := (AValue.AsScalar.Value.AsUInt64 <> 0);
skBoolean: Result := AValue.AsScalar.Value.AsBoolean; skBoolean: Result := (AValue.AsScalar.Value.AsBoolean);
else else
Result := False; Result := False;
end; end;
@@ -248,17 +250,13 @@ end;
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TAstValue; function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TAstValue;
var var
baseValue, indexValue: TAstValue; baseValue, indexValue: TAstValue;
series: TScalarRecordSeries;
rec: TScalarRecord;
index: Int64; index: Int64;
indexScalar: TScalar; indexScalar: TScalar;
begin begin
baseValue := Node.Base.Accept(Self); baseValue := Node.Base.Accept(Self);
indexValue := Node.Index.Accept(Self); indexValue := Node.Index.Accept(Self);
if (baseValue.Kind <> avkRecordSeries) then // Index validation (common for all indexable types)
raise EArgumentException.Create('Indexer `[]` can only be applied to a RecordSeries.');
if (indexValue.Kind <> avkScalar) then if (indexValue.Kind <> avkScalar) then
raise EArgumentException.Create('Indexer `[]` requires a scalar integer argument.'); raise EArgumentException.Create('Indexer `[]` requires a scalar integer argument.');
@@ -270,13 +268,54 @@ begin
raise EArgumentException.Create('Indexer `[]` requires an integer type argument.'); raise EArgumentException.Create('Indexer `[]` requires an integer type argument.');
end; end;
series := baseValue.AsRecordSeries; // Base type dispatching
case baseValue.Kind of
avkRecordSeries:
begin
var series := baseValue.AsRecordSeries;
if (index < 0) or (index >= series.TotalCount) then
raise EArgumentException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, series.TotalCount]);
if (index < 0) or (index >= series.TotalCount) then var recordValue := series.Items[Integer(index)];
raise EArgumentException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, series.TotalCount]); Result := TAstValue.FromRecord(recordValue);
end;
avkMemberSeries:
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]);
rec := series.Items[Integer(index)]; var scalarValue := memberSeries.Items[Integer(index)];
Result := TAstValue.FromRecord(rec); Result := TAstValue.FromScalar(scalarValue);
end;
else
raise EArgumentException.Create('Indexer `[]` is not supported for this value type.');
end;
end;
function TEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
var
baseValue: TAstValue;
memberName: string;
begin
baseValue := Node.Base.Accept(Self);
memberName := Node.Member.Name;
case baseValue.Kind of
avkRecordSeries:
begin
var series := baseValue.AsRecordSeries;
Result := TAstValue.FromMemberSeries(series.CreateMemberSeries(memberName));
end;
avkRecord:
begin
var recordValue := baseValue.AsRecord;
Result := TAstValue.FromScalar(recordValue.Items[memberName]);
end;
else
raise EArgumentException.Create('Member access operator `.` is not supported for this value type.');
end;
end; end;
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
@@ -607,6 +646,18 @@ begin
AppendLine(Format('} -> %s', [Result.ToString])); AppendLine(Format('} -> %s', [Result.ToString]));
end; end;
function TDebugEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
begin
AppendLine(Format('MemberAccess (Member: %s) {', [Node.Member.Name]));
Indent;
try
Result := inherited VisitMemberAccess(Node);
finally
Unindent;
end;
AppendLine(Format('} -> %s', [Result.ToString]));
end;
function TDebugEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue; function TDebugEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
begin begin
AppendLine('TernaryExpr{'); AppendLine('TernaryExpr{');
+32 -4
View File
@@ -20,8 +20,8 @@ type
function ToString: string; function ToString: string;
end; end;
// Added avkRecord to represent a TScalarRecord value. // Added avkMemberSeries to represent a TScalarMemberSeries value.
TAstValueKind = (avkUndefined, avkScalar, avkClosure, avkText, avkRecordSeries, avkRecord); TAstValueKind = (avkUndefined, avkScalar, avkClosure, avkText, avkRecordSeries, avkRecord, avkMemberSeries);
// --- Forward Declarations to break cycles --- // --- Forward Declarations to break cycles ---
IExecutionScope = interface; IExecutionScope = interface;
@@ -33,15 +33,15 @@ type
IBinaryExpressionNode = interface; IBinaryExpressionNode = interface;
IUnaryExpressionNode = interface; IUnaryExpressionNode = interface;
IIfExpressionNode = interface; IIfExpressionNode = interface;
// Added forward declaration for the new ternary expression node.
ITernaryExpressionNode = interface; ITernaryExpressionNode = interface;
ILambdaExpressionNode = interface; ILambdaExpressionNode = interface;
IFunctionCallNode = interface; IFunctionCallNode = interface;
IBlockExpressionNode = interface; IBlockExpressionNode = interface;
IVariableDeclarationNode = interface; IVariableDeclarationNode = interface;
IAssignmentNode = interface; IAssignmentNode = interface;
// Added forward declaration for the new indexer node.
IIndexerNode = interface; IIndexerNode = interface;
// Added forward declaration for the new member access node.
IMemberAccessNode = interface;
IEvaluatorClosure = interface; IEvaluatorClosure = interface;
// --- Concrete Type Definitions --- // --- Concrete Type Definitions ---
@@ -88,11 +88,13 @@ type
class function FromText(const AValue: String): TAstValue; static; class function FromText(const AValue: String): TAstValue; static;
class function FromRecordSeries(const AValue: TScalarRecordSeries): TAstValue; static; class function FromRecordSeries(const AValue: TScalarRecordSeries): TAstValue; static;
class function FromRecord(const AValue: TScalarRecord): TAstValue; static; class function FromRecord(const AValue: TScalarRecord): TAstValue; static;
class function FromMemberSeries(const AValue: TScalarMemberSeries): TAstValue; static;
function AsScalar: TScalar; function AsScalar: TScalar;
function AsClosure: IEvaluatorClosure; function AsClosure: IEvaluatorClosure;
function AsText: String; function AsText: String;
function AsRecordSeries: TScalarRecordSeries; function AsRecordSeries: TScalarRecordSeries;
function AsRecord: TScalarRecord; function AsRecord: TScalarRecord;
function AsMemberSeries: TScalarMemberSeries;
function ToString: String; function ToString: String;
property IsVoid: Boolean read GetIsVoid; property IsVoid: Boolean read GetIsVoid;
property Kind: TAstValueKind read GetKind; property Kind: TAstValueKind read GetKind;
@@ -123,6 +125,7 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
function VisitAssignment(const Node: IAssignmentNode): TAstValue; function VisitAssignment(const Node: IAssignmentNode): TAstValue;
function VisitIndexer(const Node: IIndexerNode): TAstValue; function VisitIndexer(const Node: IIndexerNode): TAstValue;
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
end; end;
IAstNode = interface(IInterface) IAstNode = interface(IInterface)
@@ -243,6 +246,16 @@ type
property Index: IExpressionNode read GetIndex; property Index: IExpressionNode read GetIndex;
end; end;
// Represents a member access expression (e.g., series.field or record.field).
IMemberAccessNode = interface(IExpressionNode)
{$region 'private'}
function GetBase: IExpressionNode;
function GetMember: IIdentifierNode;
{$endregion}
property Base: IExpressionNode read GetBase;
property Member: IIdentifierNode read GetMember;
end;
implementation implementation
uses uses
@@ -276,6 +289,13 @@ begin
Result := IEvaluatorClosure(FInterface); Result := IEvaluatorClosure(FInterface);
end; end;
function TAstValue.AsMemberSeries: TScalarMemberSeries;
begin
if (FKind <> avkMemberSeries) then
raise EInvalidCast.Create('Cannot read value as MemberSeries.');
Result := (FInterface as IVal<TScalarMemberSeries>).Value;
end;
function TAstValue.AsRecord: TScalarRecord; function TAstValue.AsRecord: TScalarRecord;
begin begin
if (FKind <> avkRecord) then if (FKind <> avkRecord) then
@@ -312,6 +332,13 @@ begin
Result.FScalar := Default(TScalar); Result.FScalar := Default(TScalar);
end; end;
class function TAstValue.FromMemberSeries(const AValue: TScalarMemberSeries): TAstValue;
begin
Result.FKind := avkMemberSeries;
Result.FInterface := TVal<TScalarMemberSeries>.Create(AValue);
Result.FScalar := Default(TScalar);
end;
class function TAstValue.FromRecord(const AValue: TScalarRecord): TAstValue; class function TAstValue.FromRecord(const AValue: TScalarRecord): TAstValue;
begin begin
Result.FKind := avkRecord; Result.FKind := avkRecord;
@@ -358,6 +385,7 @@ begin
avkText: Result := AsText; avkText: Result := AsText;
avkRecordSeries: Result := '<record_series>'; avkRecordSeries: Result := '<record_series>';
avkRecord: Result := '<record>'; avkRecord: Result := '<record>';
avkMemberSeries: Result := '<member_series>';
avkUndefined: Result := '<void>'; avkUndefined: Result := '<void>';
else else
Result := '[Unknown AstValue]'; Result := '[Unknown AstValue]';
+13
View File
@@ -34,6 +34,7 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
function VisitAssignment(const Node: IAssignmentNode): TAstValue; function VisitAssignment(const Node: IAssignmentNode): TAstValue;
function VisitIndexer(const Node: IIndexerNode): TAstValue; function VisitIndexer(const Node: IIndexerNode): TAstValue;
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
end; end;
implementation implementation
@@ -242,4 +243,16 @@ begin
Result := TAstValue.Void; Result := TAstValue.Void;
end; end;
function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
begin
AppendLine(Format('MemberAccess (Member: %s)', [Node.Member.Name]));
Indent;
AppendLine('Base:');
Indent;
Node.Base.Accept(Self);
Unindent;
Unindent;
Result := TAstValue.Void;
end;
end. end.
+45 -4
View File
@@ -22,7 +22,6 @@ type
const ACondition: IExpressionNode; const ACondition: IExpressionNode;
const AThenBranch, AElseBranch: IExpressionNode const AThenBranch, AElseBranch: IExpressionNode
): IIfExpressionNode; static; ): IIfExpressionNode; static;
// Added factory function for the new ternary expression node.
class function TernaryExpr( class function TernaryExpr(
const ACondition: IExpressionNode; const ACondition: IExpressionNode;
const AThenBranch, AElseBranch: IExpressionNode const AThenBranch, AElseBranch: IExpressionNode
@@ -33,8 +32,9 @@ type
class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; static; class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; static;
class function Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode; static; class function Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode; static;
class function AssignResult(const AValue: IExpressionNode): IAssignmentNode; static; class function AssignResult(const AValue: IExpressionNode): IAssignmentNode; static;
// Added factory for the new indexer node.
class function Indexer(const ABase: IExpressionNode; const AIndex: IExpressionNode): IIndexerNode; static; class function Indexer(const ABase: IExpressionNode; const AIndex: IExpressionNode): IIndexerNode; static;
// Added factory for the new member access node.
class function MemberAccess(const ABase: IExpressionNode; const AMember: IIdentifierNode): IMemberAccessNode; static;
end; end;
implementation implementation
@@ -186,7 +186,6 @@ type
end; end;
{ TIndexerNode } { TIndexerNode }
// Concrete implementation for the indexer node.
TIndexerNode = class(TAstNode, IIndexerNode) TIndexerNode = class(TAstNode, IIndexerNode)
private private
FBase: IExpressionNode; FBase: IExpressionNode;
@@ -198,6 +197,19 @@ type
function Accept(const Visitor: IAstVisitor): TAstValue; override; function Accept(const Visitor: IAstVisitor): TAstValue; override;
end; end;
{ TMemberAccessNode }
// Concrete implementation for the member access node.
TMemberAccessNode = class(TAstNode, IMemberAccessNode)
private
FBase: IExpressionNode;
FMember: IIdentifierNode;
function GetBase: IExpressionNode;
function GetMember: IIdentifierNode;
public
constructor Create(const ABase: IExpressionNode; const AMember: IIdentifierNode);
function Accept(const Visitor: IAstVisitor): TAstValue; override;
end;
{ TConstantNode } { TConstantNode }
constructor TConstantNode.Create(AValue: TScalar); constructor TConstantNode.Create(AValue: TScalar);
@@ -506,6 +518,30 @@ begin
Result := FIndex; Result := FIndex;
end; end;
{ TMemberAccessNode }
constructor TMemberAccessNode.Create(const ABase: IExpressionNode; const AMember: IIdentifierNode);
begin
inherited Create;
FBase := ABase;
FMember := AMember;
end;
function TMemberAccessNode.Accept(const Visitor: IAstVisitor): TAstValue;
begin
Result := Visitor.VisitMemberAccess(Self);
end;
function TMemberAccessNode.GetBase: IExpressionNode;
begin
Result := FBase;
end;
function TMemberAccessNode.GetMember: IIdentifierNode;
begin
Result := FMember;
end;
{ TAst } { TAst }
class function TAst.Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode; class function TAst.Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode;
@@ -553,7 +589,12 @@ begin
Result := TIndexerNode.Create(ABase, AIndex); Result := TIndexerNode.Create(ABase, AIndex);
end; end;
class function TAst.TernaryExpr(const ACondition, AThenBranch, AElseBranch: IExpressionNode): ITernaryExpressionNode; class function TAst.MemberAccess(const ABase: IExpressionNode; const AMember: IIdentifierNode): IMemberAccessNode;
begin
Result := TMemberAccessNode.Create(ABase, AMember);
end;
class function TAst.TernaryExpr(const ACondition: IExpressionNode; const AThenBranch, AElseBranch: IExpressionNode): ITernaryExpressionNode;
begin begin
Result := TTernaryExpressionNode.Create(ACondition, AThenBranch, AElseBranch); Result := TTernaryExpressionNode.Create(ACondition, AThenBranch, AElseBranch);
end; end;
+22 -27
View File
@@ -160,17 +160,21 @@ type
end; end;
TScalarMemberSeries = record TScalarMemberSeries = record
strict private
FKind: TScalarKind;
private private
FDef: TScalarRecordDefinition; FDef: TScalarRecordDefinition;
FArray: TChunkArray<TScalarValue>; FArray: TChunkArray<TScalarValue>;
FElement: Integer; FElement: Integer;
function GetCount: Int64; inline; function GetCount: Int64; inline;
function GetItems(Idx: Integer): TScalarValue; function GetItems(Idx: Integer): TScalar;
public public
constructor Create(const ADef: TScalarRecordDefinition; const AArray: TChunkArray<TScalarValue>; AElement: Integer); constructor Create(const ADef: TScalarRecordDefinition; const AArray: TChunkArray<TScalarValue>; AElement: Integer);
function GetKind: TScalarKind; inline;
property Count: Int64 read GetCount; property Count: Int64 read GetCount;
property Element: Integer read FElement; property Element: Integer read FElement;
property Items[Idx: Integer]: TScalarValue read GetItems; default; property Items[Idx: Integer]: TScalar read GetItems; default;
property Kind: TScalarKind read GetKind;
end; end;
// A time series of scalar records, optimized for memory and access speed. // A time series of scalar records, optimized for memory and access speed.
@@ -423,23 +427,13 @@ end;
function TScalarRecord.GetItems(const Name: String): TScalar; function TScalarRecord.GetItems(const Name: String): TScalar;
var var
i: Integer; idx: Integer;
fieldDef: TScalarRecordField;
begin begin
// Find the index of the field by name (case-insensitive) idx := FDef.IndexOf(Name);
for i := 0 to High(FDef.Fields) do if idx < 0 then
begin raise EArgumentException.CreateFmt('Field "%s" not found in record definition.', [Name]);
fieldDef := FDef.Fields[i];
if (CompareText(fieldDef.Name, Name) = 0) then
begin
// Found it. Construct the TScalar result from the kind and value.
Result.Create(fieldDef.Kind, FFields[i]);
exit;
end;
end;
// If we get here, the field was not found. Result.Create(FDef.Fields[idx].Kind, FFields[idx]);
raise EArgumentException.CreateFmt('Field "%s" not found in record definition.', [Name]);
end; end;
{ TScalarSeries } { TScalarSeries }
@@ -513,17 +507,20 @@ end;
function TScalarMemberSeries.GetCount: Int64; function TScalarMemberSeries.GetCount: Int64;
begin begin
Result := FArray.Count; Result := FArray.Count div Length(FDef.Fields);
end; end;
function TScalarMemberSeries.GetItems(Idx: Integer): TScalarValue; function TScalarMemberSeries.GetItems(Idx: Integer): TScalar;
var var
values: TArray<TScalarValue>;
fieldCount: Integer; fieldCount: Integer;
begin begin
fieldCount := Length(FDef.Fields); fieldCount := Length(FDef.Fields);
SetLength(values, fieldCount); Result.Create(FDef.Fields[FElement].Kind, FArray.Items[(FArray.Count - fieldCount) - (Idx * fieldCount) + FElement]);
Result := FArray.ItemRef[(FArray.Count - fieldCount) - (Idx * fieldCount)]^; end;
function TScalarMemberSeries.GetKind: TScalarKind;
begin
Result := FDef.Fields[FElement].Kind;
end; end;
constructor TScalarRecordDefinition.Create(const AFields: TArray<TScalarRecordField>); constructor TScalarRecordDefinition.Create(const AFields: TArray<TScalarRecordField>);
@@ -534,15 +531,13 @@ end;
function TScalarRecordDefinition.IndexOf(const Name: String): Integer; function TScalarRecordDefinition.IndexOf(const Name: String): Integer;
var var
i: Integer; i: Integer;
fieldDef: TScalarRecordField;
begin begin
// Find the index of the field by name (case-insensitive)
for i := 0 to High(FFields) do for i := 0 to High(FFields) do
begin
if (CompareText(FFields[i].Name, Name) = 0) then if (CompareText(FFields[i].Name, Name) = 0) then
exit(i); exit(i);
end;
// If we get here, the field was not found. Result := -1;
raise EArgumentException.CreateFmt('Field "%s" not found in record definition.', [Name]);
end; end;
initialization initialization