This commit is contained in:
Michael Schimmel
2025-11-03 00:35:23 +01:00
parent eb42a4ef3b
commit ec76b78b39
9 changed files with 234 additions and 32 deletions
+6
View File
@@ -56,6 +56,7 @@ type
procedure VisitCreateSeries(const Node: ICreateSeriesNode); override;
procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); override;
procedure VisitSeriesLength(const Node: ISeriesLengthNode); override;
procedure VisitNop(const Node: INopNode); override;
public
// Creates a new instance of the AST dumper.
@@ -461,6 +462,11 @@ begin
Unindent;
end;
procedure TAstDumper.VisitNop(const Node: INopNode);
begin
Log('Nop');
end;
procedure TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode);
begin
Log('SeriesLength');
+28 -6
View File
@@ -47,6 +47,7 @@ type
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual;
function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual;
function VisitNop(const Node: INopNode): TDataValue; virtual;
function IsTruthy(const AValue: TDataValue): Boolean; inline;
@@ -445,6 +446,12 @@ var
index: Int64;
series: ISeries;
recSeries: IRecordSeries;
i, fieldCount: Integer;
values: TArray<TScalar.TValue>;
key: IKeyword;
memberSeries: ISeries;
scalarValue: TScalar;
rec: TScalarRecord;
begin
baseValue := Node.Base.Accept(Self);
indexValue := Node.Index.Accept(Self);
@@ -467,12 +474,20 @@ begin
recSeries := baseValue.AsRecordSeries;
if (index < 0) or (index >= recSeries.TotalCount) then
raise EArgumentException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, recSeries.TotalCount]);
// Accessing a record series by index materializes the TScalarRecord
var rec := TScalarRecord.Create(recSeries.Def, nil); // Nil fields, needs implementation
// TODO: This path (materializing a record from TScalarRecordSeries) is not fully implemented.
// We need to fetch the underlying TScalar.TValue array slice.
// For now, returning an empty record shell.
raise ENotSupportedException.Create('Indexing a RecordSeries to materialize a Record is not yet implemented.');
// Materialize the TScalarRecord by accessing each member series by index
fieldCount := Length(recSeries.Def.Fields);
SetLength(values, fieldCount);
for i := 0 to fieldCount - 1 do
begin
key := recSeries.Def.Fields[i].Key;
memberSeries := recSeries[key];
scalarValue := memberSeries[index];
values[i] := scalarValue.Value;
end;
rec := TScalarRecord.Create(recSeries.Def, values);
Result := TDataValue.FromRecord(rec);
end;
else
@@ -646,6 +661,13 @@ begin
Result := expression.Accept(Self);
end;
function TEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue;
begin
// This node should be rejected by the Binder/TypeChecker.
// If it reaches the evaluator, it's a compiler bug, but we treat it as Void.
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
var
seriesValue: TDataValue;
+17
View File
@@ -51,6 +51,8 @@ type
function JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode;
function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
function JsonToNopNode(const AObj: TJSONObject): INopNode;
protected
// IAstVisitor implementation for serialization (T = TJSONObject)
function VisitConstant(const Node: IConstantNode): TJSONObject; override;
@@ -77,6 +79,7 @@ type
function VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject; override;
function VisitNop(const Node: INopNode): TJSONObject; override;
function Serialize(const RootNode: IAstNode): TJSONObject;
function Deserialize(const AJson: TJSONObject): IAstNode;
@@ -456,6 +459,13 @@ begin
Result.AddPair('Series', seriesObj);
end;
function TJsonAstConverter.VisitNop(const Node: INopNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Nop'));
// No other properties needed, as it is stateless
end;
{ TJsonAstConverter - Deserialization }
function TJsonAstConverter.JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue;
@@ -761,6 +771,11 @@ begin
Result := TAst.SeriesLength(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject));
end;
function TJsonAstConverter.JsonToNopNode(const AObj: TJSONObject): INopNode;
begin
Result := TAst.Nop.AsNop;
end;
function TJsonAstConverter.JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode;
var
obj: TJSONObject;
@@ -823,6 +838,8 @@ begin
Result := JsonToAddSeriesItemNode(obj)
else if nodeType = 'SeriesLength' then
Result := JsonToSeriesLengthNode(obj)
else if nodeType = 'Nop' then // Added Nop Deserialization logic
Result := JsonToNopNode(obj)
else
raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s" for JSON deserialization.', [nodeType]);
end;
+10 -1
View File
@@ -37,6 +37,7 @@ type
IAddSeriesItemNode = interface;
ISeriesLengthNode = interface;
IRecurNode = interface;
INopNode = interface; // Added Nop
// Defines the concrete kinds of AST nodes
TAstNodeKind = (
@@ -63,7 +64,8 @@ type
akCreateSeries,
akAddSeriesItem,
akSeriesLength,
akRecur
akRecur,
akNop // Added Nop
);
// --- Concrete Type Definitions ---
@@ -113,6 +115,7 @@ type
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
function VisitRecurNode(const Node: IRecurNode): TDataValue;
function VisitNop(const Node: INopNode): TDataValue; // Added Nop
end;
IAstNode = interface(IInterface)
@@ -145,10 +148,16 @@ type
function AsAddSeriesItem: IAddSeriesItemNode;
function AsSeriesLength: ISeriesLengthNode;
function AsRecur: IRecurNode;
function AsNop: INopNode; // Added Nop
property Kind: TAstNodeKind read GetKind;
end;
INopNode = interface(IAstNode)
// A placeholder node for the UI (e.g., drag target).
// This node is illegal during compilation.
end;
IConstantNode = interface(IAstNode)
// Represents a constant value in the AST (Scalar, Text, or Void).
{$region 'private'}
+27 -1
View File
@@ -9,6 +9,7 @@ uses
var
// This BNF should always be kept up to date and valid. It is used to comunicate with LLMs.
// (Nop isn't part of the language syntax.)
BNF: String =
'''
@@ -221,6 +222,7 @@ type
procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); override;
procedure VisitSeriesLength(const Node: ISeriesLengthNode); override;
procedure VisitRecurNode(const Node: IRecurNode); override;
procedure VisitNop(const Node: INopNode); override;
end;
{ TLexer }
@@ -716,7 +718,24 @@ begin
end;
tkIdentifier:
begin
Result.Node := TAst.Identifier(FCurrentToken.Text);
// Check for the NOP token ('...')
if FCurrentToken.Text = '...' then
begin
// The token '...' represents the Nop node, a non-compilable placeholder used exclusively
// for visual representations (e.g., UI drag targets).
//
// We handle it as a special case of tkIdentifier instead of introducing a dedicated tkNop
// TokenKind because of its **unofficial and transient status**.
//
// This allows the Parser to immediately apply the syntactic policy: mapping this
// specific identifier string directly to the non-standard TAst.Nop node, ensuring
// it bypasses the regular identifier creation and maintains its "placeholder" identity.
Result.Node := TAst.Nop;
end
else
begin
Result.Node := TAst.Identifier(FCurrentToken.Text);
end;
NextToken;
end;
tkLeftParen: Result.Node := ParseList;
@@ -1068,6 +1087,13 @@ begin
Append(')');
end;
procedure TPrettyPrintVisitor.VisitNop(const Node: INopNode);
begin
// A placeholder node, usually for UI.
//TODO Nop soll durch ein spezielles ...-Token repräsentiert werden. Analysiere, ob dies syntaktisch möglich ist. Wenn ja, soll der Parser "inoffiziell" in der Lage sein Nops zu lesen und zu erzeugen.
Append('...');
end;
procedure TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode);
begin
Append('(count ');
+25
View File
@@ -37,6 +37,7 @@ type
function IAstVisitor.VisitAddSeriesItem = DoVisitAddSeriesItem;
function IAstVisitor.VisitSeriesLength = DoVisitSeriesLength;
function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
function IAstVisitor.VisitNop = DoVisitNop; // Added Nop
// Private bridge method implementations
function DoVisitConstant(const Node: IConstantNode): TDataValue;
@@ -63,6 +64,7 @@ type
function DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
function DoVisitRecurNode(const Node: IRecurNode): TDataValue;
function DoVisitNop(const Node: INopNode): TDataValue; // Added Nop
protected
// Visit a node.
@@ -92,6 +94,7 @@ type
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; virtual; abstract;
function VisitSeriesLength(const Node: ISeriesLengthNode): T; virtual; abstract;
function VisitRecurNode(const Node: IRecurNode): T; virtual; abstract;
function VisitNop(const Node: INopNode): T; virtual; abstract; // Added Nop
end;
TAstTransformer = class abstract(TAstVisitor<IAstNode>)
@@ -123,6 +126,7 @@ type
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
function VisitNop(const Node: INopNode): IAstNode; override; // Added Nop
end;
TAstVisitor = class abstract(TInterfacedObject, IAstVisitor)
@@ -152,6 +156,7 @@ type
function IAstVisitor.VisitAddSeriesItem = DoVisitAddSeriesItem;
function IAstVisitor.VisitSeriesLength = DoVisitSeriesLength;
function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
function IAstVisitor.VisitNop = DoVisitNop; // Added Nop
// Private bridge method implementations
function DoVisitConstant(const Node: IConstantNode): TDataValue;
@@ -178,6 +183,7 @@ type
function DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
function DoVisitRecurNode(const Node: IRecurNode): TDataValue;
function DoVisitNop(const Node: INopNode): TDataValue; // Added Nop
protected
// Virtual procedures for descendants (Interpreters/Side-effects) to override
@@ -205,6 +211,7 @@ type
procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); virtual; abstract;
procedure VisitSeriesLength(const Node: ISeriesLengthNode); virtual; abstract;
procedure VisitRecurNode(const Node: IRecurNode); virtual; abstract;
procedure VisitNop(const Node: INopNode); virtual; abstract; // Added Nop
end;
implementation
@@ -341,6 +348,12 @@ begin
Result := TDataValue.FromGeneric<T>(VisitRecurNode(Node));
end;
function TAstVisitor<T>.DoVisitNop(const Node: INopNode): TDataValue;
begin
// Added Nop implementation
Result := TDataValue.FromGeneric<T>(VisitNop(Node));
end;
function TAstTransformer.AcceptParameters(const Nodes: TArray<IIdentifierNode>): TArray<IIdentifierNode>;
var
i: Integer;
@@ -418,6 +431,12 @@ begin
Result := Node; // Leaf node
end;
function TAstTransformer.VisitNop(const Node: INopNode): IAstNode;
begin
// Added Nop implementation
Result := Node; // Leaf node
end;
function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode;
var
N: TBinaryExpressionNode;
@@ -753,4 +772,10 @@ begin
VisitRecurNode(Node);
end;
function TAstVisitor.DoVisitNop(const Node: INopNode): TDataValue;
begin
// Added Nop implementation
VisitNop(Node);
end;
end.
+52
View File
@@ -28,6 +28,10 @@ type
class procedure RegisterLibrary(const AProc: TRegisterLibraryProc); static;
class function CreateScope(Parent: IExecutionScope; const Descriptor: IScopeDescriptor = nil): IExecutionScope; static;
// A No-Operation node, used as a placeholder/stub (e.g., for UI drop targets).
// This node is illegal in the compiler (Binder/TypeChecker will reject it).
class function Nop: IAstNode; static;
// --- Factory functions ---
class function Constant(const AValue: TDataValue): IConstantNode; overload; static;
class function Constant(const AValue: String): IConstantNode; overload; static;
@@ -103,6 +107,7 @@ type
function AsAddSeriesItem: IAddSeriesItemNode; virtual;
function AsSeriesLength: ISeriesLengthNode; virtual;
function AsRecur: IRecurNode; virtual;
function AsNop: INopNode; virtual; // Added Nop
property StaticType: IStaticType read FStaticType write SetStaticType;
property Kind: TAstNodeKind read GetKind;
@@ -474,6 +479,17 @@ implementation
uses
System.Generics.Defaults;
// Added Nop
type
TNopNode = class(TAstNode, INopNode)
private
function GetKind: TAstNodeKind; override;
public
constructor Create;
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsNop: INopNode; override;
end;
{ TAst }
class function TAst.CreateScope(Parent: IExecutionScope; const Descriptor: IScopeDescriptor): IExecutionScope;
@@ -603,6 +619,12 @@ begin
Result := TMemberAccessNode.Create(ABase, AMember);
end;
class function TAst.Nop: IAstNode;
begin
// Factory function for the new Nop node
Result := TNopNode.Create;
end;
class function TAst.Quasiquote(const AExpression: IAstNode): IQuasiquoteNode;
begin
Result := TQuasiquoteNode.Create(AExpression);
@@ -650,6 +672,30 @@ begin
Result := TVariableDeclarationNode.Create(AIdentifier, AInitializer);
end;
{ TNopNode }
constructor TNopNode.Create;
begin
inherited Create;
// Nop nodes are 'Void' by default, though TypeChecker will reject them anyway.
StaticType := TTypes.Void;
end;
function TNopNode.Accept(const Visitor: IAstVisitor): TDataValue;
begin
Result := Visitor.VisitNop(Self);
end;
function TNopNode.AsNop: INopNode;
begin
Result := Self;
end;
function TNopNode.GetKind: TAstNodeKind;
begin
Result := akNop;
end;
{ TAstNode }
constructor TAstNode.Create;
@@ -733,6 +779,12 @@ begin
raise ETypeException.Create('Node is not a MemberAccess');
end;
function TAstNode.AsNop: INopNode;
begin
// Added Nop implementation
raise ETypeException.Create('Node is not a Nop');
end;
function TAstNode.AsQuasiquote: IQuasiquoteNode;
begin
raise ETypeException.Create('Node is not a Quasiquote');