Ast development
This commit is contained in:
+36
-37
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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{');
|
||||||
|
|||||||
@@ -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]';
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user