Major refactoring, split Bound Ast from source Ast
This commit is contained in:
+342
-341
@@ -8,16 +8,25 @@ uses
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Traverser,
|
||||
Myc.Ast.Scope;
|
||||
Myc.Ast.Transformer,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
TAstBinder = class(TAstTraverser)
|
||||
// The binder is a transformer that enriches the AST with semantic information
|
||||
// like resolved addresses, scopes, and tail-call annotations.
|
||||
IAstBinder = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
|
||||
end;
|
||||
|
||||
TAstBinder = class(TAstTransformer, IAstBinder)
|
||||
private
|
||||
type
|
||||
// Helper class to track upvalues for a lambda expression.
|
||||
TUpvalueMapping = class
|
||||
public
|
||||
Map: TDictionary<TResolvedAddress, Integer>;
|
||||
Nodes: TList<IIdentifierNode>;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
@@ -27,60 +36,87 @@ type
|
||||
FNestedLambdaCount: Integer;
|
||||
FIsTailStack: TStack<Boolean>;
|
||||
FNextIsTail: Boolean;
|
||||
|
||||
procedure EnterScope;
|
||||
procedure ExitScope;
|
||||
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
function GetCurrentDescriptor: IScopeDescriptor;
|
||||
function IsValidIdentifier(const Name: string): Boolean;
|
||||
protected
|
||||
// Stack management for tail-call state is centralized here.
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
|
||||
public
|
||||
constructor Create(const AInitialScope: IExecutionScope);
|
||||
destructor Destroy; override;
|
||||
|
||||
class function Bind(const RootNode: IAstNode; const ParentScope: IExecutionScope): IScopeDescriptor;
|
||||
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
|
||||
|
||||
class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; static;
|
||||
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
// The binder overrides specific transform methods to enrich the AST.
|
||||
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; override;
|
||||
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; override;
|
||||
function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; override;
|
||||
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; override;
|
||||
function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; override;
|
||||
function TransformRecur(const Node: IRecurNode): IRecurNode; override;
|
||||
function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; override;
|
||||
function TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; override;
|
||||
function TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; override;
|
||||
function TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; override;
|
||||
function TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; override;
|
||||
|
||||
property CurrentDescriptor: IScopeDescriptor read FCurrentDescriptor;
|
||||
property CurrentDescriptor: IScopeDescriptor read GetCurrentDescriptor;
|
||||
end;
|
||||
|
||||
TBoundIdentifierNode = class(TIdentifierNode)
|
||||
private
|
||||
FAddress: TResolvedAddress;
|
||||
public
|
||||
constructor Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress);
|
||||
property Address: TResolvedAddress read FAddress;
|
||||
end;
|
||||
|
||||
TBoundLambdaExpressionNode = class(TLambdaExpressionNode)
|
||||
private
|
||||
FScopeDescriptor: IScopeDescriptor;
|
||||
FUpvalues: TArray<TResolvedAddress>;
|
||||
FHasNestedLambdas: Boolean;
|
||||
public
|
||||
constructor Create(
|
||||
const AUnboundNode: ILambdaExpressionNode;
|
||||
const ABody: IAstNode;
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
const AScopeDescriptor: IScopeDescriptor;
|
||||
const AUpvalues: TArray<TResolvedAddress>;
|
||||
AHasNestedLambdas: Boolean
|
||||
);
|
||||
|
||||
property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor;
|
||||
property Upvalues: TArray<TResolvedAddress> read FUpvalues;
|
||||
property HasNestedLambdas: Boolean read FHasNestedLambdas;
|
||||
end;
|
||||
|
||||
TBoundFunctionCallNode = class(TFunctionCallNode)
|
||||
private
|
||||
FIsTailCall: Boolean;
|
||||
public
|
||||
constructor Create(
|
||||
const AUnboundNode: IFunctionCallNode;
|
||||
const ACallee: IAstNode;
|
||||
const AArguments: TArray<IAstNode>;
|
||||
AIsTailCall: Boolean
|
||||
);
|
||||
property IsTailCall: Boolean read FIsTailCall;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.Generics.Defaults,
|
||||
System.Character,
|
||||
Myc.Ast;
|
||||
System.Character;
|
||||
|
||||
type
|
||||
TScopeDescriptor = class(TInterfacedObject, IScopeDescriptor)
|
||||
private
|
||||
FParent: IScopeDescriptor;
|
||||
FSymbols: TDictionary<string, Integer>;
|
||||
function GetParent: IScopeDescriptor;
|
||||
function GetSlotCount: Integer;
|
||||
function GetSymbols: TDictionary<string, Integer>;
|
||||
public
|
||||
constructor Create(const AParent: IScopeDescriptor);
|
||||
destructor Destroy; override;
|
||||
function Define(const Name: string): Integer;
|
||||
function FindSymbol(const Name: string; out Depth, Index: Integer): Boolean;
|
||||
function CreateScope(const Parent: IExecutionScope): IExecutionScope;
|
||||
procedure PopulateFromScope(Scope: TExecutionScope);
|
||||
property Symbols: TDictionary<string, Integer> read FSymbols;
|
||||
end;
|
||||
|
||||
// A custom equality comparer for TResolvedAddress to ensure correct behavior in TDictionary.
|
||||
TResolvedAddressComparer = class(TEqualityComparer<TResolvedAddress>)
|
||||
public
|
||||
@@ -88,73 +124,68 @@ type
|
||||
function GetHashCode(const Value: TResolvedAddress): Integer; override;
|
||||
end;
|
||||
|
||||
{ TResolvedAddressComparer }
|
||||
{ TBoundIdentifierNode }
|
||||
constructor TBoundIdentifierNode.Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress);
|
||||
begin
|
||||
inherited Create(AUnboundNode.Name);
|
||||
FAddress := AAddress;
|
||||
end;
|
||||
|
||||
{ TBoundLambdaExpressionNode }
|
||||
constructor TBoundLambdaExpressionNode.Create(
|
||||
const AUnboundNode: ILambdaExpressionNode;
|
||||
const ABody: IAstNode;
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
const AScopeDescriptor: IScopeDescriptor;
|
||||
const AUpvalues: TArray<TResolvedAddress>;
|
||||
AHasNestedLambdas: Boolean
|
||||
);
|
||||
begin
|
||||
inherited Create(AParameters, ABody);
|
||||
FScopeDescriptor := AScopeDescriptor;
|
||||
FUpvalues := AUpvalues;
|
||||
FHasNestedLambdas := AHasNestedLambdas;
|
||||
end;
|
||||
|
||||
{ TBoundFunctionCallNode }
|
||||
constructor TBoundFunctionCallNode.Create(
|
||||
const AUnboundNode: IFunctionCallNode;
|
||||
const ACallee: IAstNode;
|
||||
const AArguments: TArray<IAstNode>;
|
||||
AIsTailCall: Boolean
|
||||
);
|
||||
begin
|
||||
inherited Create(ACallee, AArguments);
|
||||
FIsTailCall := AIsTailCall;
|
||||
end;
|
||||
|
||||
{ TResolvedAddressComparer }
|
||||
function TResolvedAddressComparer.Equals(const Left, Right: TResolvedAddress): Boolean;
|
||||
begin
|
||||
// Use the existing equality operator for the record.
|
||||
Result := (Left = Right);
|
||||
end;
|
||||
|
||||
function TResolvedAddressComparer.GetHashCode(const Value: TResolvedAddress): Integer;
|
||||
begin
|
||||
// Classic hash combining algorithm using prime numbers.
|
||||
Result := 17;
|
||||
Result := Result * 23 + Ord(Value.Kind);
|
||||
Result := Result * 23 + Value.ScopeDepth;
|
||||
Result := Result * 23 + Value.SlotIndex;
|
||||
end;
|
||||
|
||||
{ TAstBinder }
|
||||
|
||||
constructor TAstBinder.Create(const AInitialScope: IExecutionScope);
|
||||
{ TAstBinder.TUpvalueMapping }
|
||||
constructor TAstBinder.TUpvalueMapping.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FCurrentDescriptor := CreateDescriptor(AInitialScope);
|
||||
FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(true);
|
||||
FNestedLambdaCount := 0;
|
||||
FIsTailStack := TStack<Boolean>.Create;
|
||||
// The content of the root node is in a tail position.
|
||||
FNextIsTail := true;
|
||||
Map := TDictionary<TResolvedAddress, Integer>.Create(TResolvedAddressComparer.Create);
|
||||
Nodes := TList<IIdentifierNode>.Create();
|
||||
end;
|
||||
|
||||
destructor TAstBinder.Destroy;
|
||||
destructor TAstBinder.TUpvalueMapping.Destroy;
|
||||
begin
|
||||
FIsTailStack.Free;
|
||||
FUpvalueStack.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if not Assigned(Node) or Done then
|
||||
exit;
|
||||
|
||||
FIsTailStack.Push(FNextIsTail);
|
||||
try
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
FNextIsTail := FIsTailStack.Pop;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TAstBinder.Bind(const RootNode: IAstNode; const ParentScope: IExecutionScope): IScopeDescriptor;
|
||||
var
|
||||
binder: TAstBinder;
|
||||
begin
|
||||
binder := TAstBinder.Create(ParentScope);
|
||||
try
|
||||
binder.EnterScope;
|
||||
try
|
||||
// Start the traversal
|
||||
binder.Accept(RootNode);
|
||||
Result := binder.CurrentDescriptor;
|
||||
finally
|
||||
binder.ExitScope;
|
||||
end;
|
||||
finally
|
||||
binder.Free;
|
||||
end;
|
||||
Nodes.Free;
|
||||
Map.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
class function TAstBinder.CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor;
|
||||
@@ -169,318 +200,288 @@ begin
|
||||
Result := TScopeDescriptor.Create(nil);
|
||||
end;
|
||||
|
||||
constructor TAstBinder.Create(const AInitialScope: IExecutionScope);
|
||||
begin
|
||||
inherited Create;
|
||||
FCurrentDescriptor := CreateDescriptor(AInitialScope);
|
||||
FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(True);
|
||||
FNestedLambdaCount := 0;
|
||||
FIsTailStack := TStack<Boolean>.Create;
|
||||
FNextIsTail := True;
|
||||
end;
|
||||
|
||||
destructor TAstBinder.Destroy;
|
||||
begin
|
||||
FIsTailStack.Free;
|
||||
FUpvalueStack.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if (not Assigned(Node)) or Done then
|
||||
exit;
|
||||
|
||||
FIsTailStack.Push(FNextIsTail);
|
||||
try
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
FNextIsTail := FIsTailStack.Pop;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstBinder.EnterScope;
|
||||
begin
|
||||
FCurrentDescriptor := TScopeDescriptor.Create(FCurrentDescriptor);
|
||||
end;
|
||||
|
||||
function TAstBinder.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
|
||||
begin
|
||||
EnterScope;
|
||||
try
|
||||
Result := Accept(RootNode).AsIntf<IAstNode>;
|
||||
Descriptor := FCurrentDescriptor;
|
||||
finally
|
||||
ExitScope;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstBinder.ExitScope;
|
||||
begin
|
||||
FCurrentDescriptor := FCurrentDescriptor.Parent;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
function TAstBinder.GetCurrentDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
|
||||
var n := Node.Expressions.Count - 1;
|
||||
for var i := 0 to n do
|
||||
begin
|
||||
// The last expression is in a tail position IF the block itself is.
|
||||
if i = n then
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
|
||||
Accept(Node.Expressions[i]);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
begin
|
||||
// Annotate this node based on its context, which is on top of the stack.
|
||||
(Node as TFunctionCallNode).IsTailCall := FIsTailStack.Peek;
|
||||
|
||||
// Let the default traverser visit children (callee, args), but ensure
|
||||
// their context is non-tail.
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
begin
|
||||
// Check if the current context is a tail position.
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
|
||||
// Arguments to recur are not in a tail position.
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
var
|
||||
depth, idx: Integer;
|
||||
identNode: TIdentifierNode;
|
||||
upvalue: TUpvalueMapping;
|
||||
originalAddress: TResolvedAddress;
|
||||
upvalueIndex: Integer;
|
||||
begin
|
||||
identNode := Node as TIdentifierNode;
|
||||
if identNode.Address.Kind <> akUnresolved then
|
||||
exit;
|
||||
|
||||
if FCurrentDescriptor.FindSymbol(identNode.Name, depth, idx) then
|
||||
begin
|
||||
if (depth > 0) and (FUpvalueStack.Count > 0) then
|
||||
begin
|
||||
upvalue := FUpvalueStack.Peek;
|
||||
|
||||
// Address is relative to the lambda's parent scope.
|
||||
dec(depth);
|
||||
originalAddress := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
|
||||
if not upvalue.Map.TryGetValue(originalAddress, upvalueIndex) then
|
||||
begin
|
||||
upvalueIndex := upvalue.Map.Count;
|
||||
upvalue.Map.Add(originalAddress, upvalueIndex);
|
||||
end;
|
||||
|
||||
(Node as TIdentifierNode).Address := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// 1. case: depth=0 - this is a local var
|
||||
// 2. case: UpvalueStack is empty - there is no surrounding lambda, we need to reference (and capture) the whole parent scope
|
||||
(Node as TIdentifierNode).Address := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise Exception.CreateFmt('Undefined identifier: "%s"', [identNode.Name]);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
begin
|
||||
// The condition is never in a tail position.
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Condition);
|
||||
|
||||
// The branches are in a tail position if the if-expression itself is.
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
Accept(Node.ThenBranch);
|
||||
if Assigned(Node.ElseBranch) then
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
var
|
||||
param: IIdentifierNode;
|
||||
sourceAddresses: TArray<TResolvedAddress>;
|
||||
sortedPairs: TArray<TPair<TResolvedAddress, Integer>>;
|
||||
begin
|
||||
FUpvalueStack.Push(TUpvalueMapping.Create);
|
||||
try
|
||||
EnterScope;
|
||||
try
|
||||
// Reserve slot 0 for the closure itself (for 'recur'),
|
||||
// using a name that cannot be accessed from source code.
|
||||
FCurrentDescriptor.Define('<self>');
|
||||
|
||||
for param in Node.Parameters do
|
||||
FCurrentDescriptor.Define(param.Name);
|
||||
|
||||
var lastNestedLambdaCount := FNestedLambdaCount;
|
||||
|
||||
// Parameters are never in a tail position.
|
||||
FNextIsTail := False;
|
||||
for param in Node.Parameters do
|
||||
Accept(param);
|
||||
|
||||
// The body of a lambda is always in a tail position.
|
||||
FNextIsTail := True;
|
||||
Accept(Node.Body);
|
||||
|
||||
(Node as TLambdaExpressionNode).HasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
|
||||
(Node as TLambdaExpressionNode).ScopeDescriptor := FCurrentDescriptor;
|
||||
finally
|
||||
ExitScope;
|
||||
end;
|
||||
finally
|
||||
var upvalue := FUpvalueStack.Peek;
|
||||
try
|
||||
sortedPairs := upvalue.Map.ToArray;
|
||||
TArray.Sort<TPair<TResolvedAddress, Integer>>(
|
||||
sortedPairs,
|
||||
TComparer<TPair<TResolvedAddress, Integer>>.Construct(
|
||||
function(const Left, Right: TPair<TResolvedAddress, Integer>): Integer begin Result := Left.Value - Right.Value; end
|
||||
)
|
||||
);
|
||||
|
||||
SetLength(sourceAddresses, Length(sortedPairs));
|
||||
for var i := 0 to High(sortedPairs) do
|
||||
sourceAddresses[i] := sortedPairs[i].Key;
|
||||
|
||||
(Node as TLambdaExpressionNode).Upvalues := sourceAddresses;
|
||||
finally
|
||||
FUpvalueStack.Pop;
|
||||
end;
|
||||
|
||||
inc(FNestedLambdaCount);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
begin
|
||||
// The condition is never in a tail position.
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Condition);
|
||||
|
||||
// The branches are in a tail position if the ternary expression itself is.
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
Accept(Node.ThenBranch);
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
Result := FCurrentDescriptor;
|
||||
end;
|
||||
|
||||
function TAstBinder.IsValidIdentifier(const Name: string): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
c: Char;
|
||||
begin
|
||||
if Name.IsEmpty then
|
||||
exit(False);
|
||||
|
||||
// First character must be a letter or underscore.
|
||||
c := Name[1];
|
||||
if not (c.IsLetter or (c = '_')) then
|
||||
exit(False);
|
||||
|
||||
// Subsequent characters can be letters, numbers, underscore, or hyphen.
|
||||
for i := 2 to Length(Name) do
|
||||
for c in Name do
|
||||
begin
|
||||
c := Name[i];
|
||||
if not (c.IsLetterOrDigit or (c = '_') or (c = '-')) then
|
||||
exit(False);
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function TAstBinder.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
|
||||
var
|
||||
slotIndex: Integer;
|
||||
depth, idx: Integer;
|
||||
begin
|
||||
// The initializer expression is never in a tail position.
|
||||
FNextIsTail := False;
|
||||
if Assigned(Node.Initializer) then
|
||||
Accept(Node.Initializer);
|
||||
if FCurrentDescriptor.FindSymbol(Node.Name, depth, idx) then
|
||||
begin
|
||||
if (depth > 0) and (FUpvalueStack.Count > 0) then
|
||||
begin
|
||||
var upvalue := FUpvalueStack.Peek;
|
||||
dec(depth);
|
||||
var originalAddress := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
|
||||
// Reject identifiers that contain special characters or reserved operator characters.
|
||||
var upvalueIndex: Integer;
|
||||
if not upvalue.Map.TryGetValue(originalAddress, upvalueIndex) then
|
||||
begin
|
||||
upvalueIndex := upvalue.Map.Count;
|
||||
upvalue.Map.Add(originalAddress, upvalueIndex);
|
||||
end;
|
||||
var address := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
|
||||
Result := TBoundIdentifierNode.Create(Node, address);
|
||||
end
|
||||
else
|
||||
begin
|
||||
var address := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
Result := TBoundIdentifierNode.Create(Node, address);
|
||||
end
|
||||
end
|
||||
else
|
||||
raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
|
||||
var
|
||||
initializer: IAstNode;
|
||||
slotIndex: Integer;
|
||||
address: TResolvedAddress;
|
||||
boundIdentifier: IIdentifierNode;
|
||||
begin
|
||||
if not IsValidIdentifier(Node.Identifier.Name) then
|
||||
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
|
||||
|
||||
FNextIsTail := False;
|
||||
if Node.Initializer <> nil then
|
||||
initializer := Accept(Node.Initializer).AsIntf<IAstNode>;
|
||||
|
||||
slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name);
|
||||
(Node.Identifier as TIdentifierNode).Address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
|
||||
boundIdentifier := TBoundIdentifierNode.Create(Node.Identifier, address);
|
||||
|
||||
Result := TAst.VarDecl(boundIdentifier, initializer);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformAssignment(const Node: IAssignmentNode): IAssignmentNode;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformAssignment(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformBinaryExpression(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformUnaryExpression(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
|
||||
var
|
||||
i: integer;
|
||||
boundParams: TArray<IIdentifierNode>;
|
||||
boundBody: IAstNode;
|
||||
lambdaScope: IScopeDescriptor;
|
||||
upvalues: TArray<TResolvedAddress>;
|
||||
hasNestedLambdas: Boolean;
|
||||
lastNestedLambdaCount: Integer;
|
||||
begin
|
||||
FUpvalueStack.Push(TUpvalueMapping.Create);
|
||||
try
|
||||
EnterScope;
|
||||
try
|
||||
FCurrentDescriptor.Define('<self>');
|
||||
|
||||
SetLength(boundParams, Length(Node.Parameters));
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
begin
|
||||
var paramNode := Node.Parameters[i];
|
||||
var slotIndex := FCurrentDescriptor.Define(paramNode.Name);
|
||||
var address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
boundParams[i] := TBoundIdentifierNode.Create(paramNode, address);
|
||||
end;
|
||||
|
||||
lastNestedLambdaCount := FNestedLambdaCount;
|
||||
|
||||
FNextIsTail := True;
|
||||
boundBody := Accept(Node.Body).AsIntf<IAstNode>;
|
||||
|
||||
hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
|
||||
lambdaScope := FCurrentDescriptor;
|
||||
finally
|
||||
ExitScope;
|
||||
end;
|
||||
|
||||
var upvalueMapping := FUpvalueStack.Peek;
|
||||
var sortedPairs := upvalueMapping.Map.ToArray;
|
||||
TArray.Sort<TPair<TResolvedAddress, Integer>>(
|
||||
sortedPairs,
|
||||
TComparer<TPair<TResolvedAddress, Integer>>.Construct(
|
||||
function(const Left, Right: TPair<TResolvedAddress, Integer>): Integer begin Result := Left.Value - Right.Value; end
|
||||
)
|
||||
);
|
||||
|
||||
SetLength(upvalues, Length(sortedPairs));
|
||||
for i := 0 to High(sortedPairs) do
|
||||
upvalues[i] := sortedPairs[i].Key;
|
||||
|
||||
finally
|
||||
FUpvalueStack.Pop;
|
||||
end;
|
||||
|
||||
inc(FNestedLambdaCount);
|
||||
Result := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode;
|
||||
var
|
||||
isTailCall: Boolean;
|
||||
callee: IAstNode;
|
||||
args: TArray<IAstNode>;
|
||||
begin
|
||||
isTailCall := FIsTailStack.Peek;
|
||||
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Identifier);
|
||||
callee := Accept(Node.Callee).AsIntf<IAstNode>;
|
||||
args := TransformNodes<IAstNode>(Node.Arguments);
|
||||
|
||||
Result := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall);
|
||||
end;
|
||||
|
||||
{ TScopeDescriptor }
|
||||
|
||||
constructor TScopeDescriptor.Create(const AParent: IScopeDescriptor);
|
||||
function TAstBinder.TransformRecur(const Node: IRecurNode): IRecurNode;
|
||||
begin
|
||||
inherited Create;
|
||||
FParent := AParent;
|
||||
FSymbols := TDictionary<string, Integer>.Create;
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformRecur(Node);
|
||||
end;
|
||||
|
||||
destructor TScopeDescriptor.Destroy;
|
||||
begin
|
||||
FSymbols.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.Define(const Name: string): Integer;
|
||||
begin
|
||||
Result := FSymbols.Count;
|
||||
FSymbols.Add(Name, Result);
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.FindSymbol(const Name: string; out Depth, Index: Integer): Boolean;
|
||||
function TAstBinder.TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode;
|
||||
var
|
||||
currentDescriptor: TScopeDescriptor;
|
||||
exprs: TArray<IAstNode>;
|
||||
i: Integer;
|
||||
isContextTail: Boolean;
|
||||
begin
|
||||
Depth := 0;
|
||||
currentDescriptor := Self;
|
||||
while currentDescriptor <> nil do
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
SetLength(exprs, Node.Expressions.Count);
|
||||
for i := 0 to Node.Expressions.Count - 1 do
|
||||
begin
|
||||
if currentDescriptor.FSymbols.TryGetValue(Name, Index) then
|
||||
exit(true);
|
||||
inc(Depth);
|
||||
currentDescriptor := currentDescriptor.FParent as TScopeDescriptor;
|
||||
FNextIsTail := isContextTail and (i = Node.Expressions.Count - 1);
|
||||
exprs[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
|
||||
end;
|
||||
Result := False;
|
||||
Result := TAst.Block(exprs);
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.GetParent: IScopeDescriptor;
|
||||
function TAstBinder.TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode;
|
||||
var
|
||||
isContextTail: Boolean;
|
||||
condition, thenBranch, elseBranch: IAstNode;
|
||||
begin
|
||||
Result := FParent;
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
FNextIsTail := False;
|
||||
condition := Accept(Node.Condition).AsIntf<IAstNode>;
|
||||
|
||||
FNextIsTail := isContextTail;
|
||||
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
|
||||
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
|
||||
|
||||
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
|
||||
Result := TAst.IfExpr(condition, thenBranch, elseBranch)
|
||||
else
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.GetSlotCount: Integer;
|
||||
function TAstBinder.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode;
|
||||
var
|
||||
isContextTail: Boolean;
|
||||
condition, thenBranch, elseBranch: IAstNode;
|
||||
begin
|
||||
Result := FSymbols.Count;
|
||||
end;
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
function TScopeDescriptor.GetSymbols: TDictionary<string, Integer>;
|
||||
begin
|
||||
Result := FSymbols;
|
||||
end;
|
||||
FNextIsTail := False;
|
||||
condition := Accept(Node.Condition).AsIntf<IAstNode>;
|
||||
|
||||
function TScopeDescriptor.CreateScope(const Parent: IExecutionScope): IExecutionScope;
|
||||
begin
|
||||
Result := TExecutionScope.Create(Parent, Self, nil);
|
||||
end;
|
||||
FNextIsTail := isContextTail;
|
||||
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
|
||||
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
|
||||
|
||||
procedure TScopeDescriptor.PopulateFromScope(Scope: TExecutionScope);
|
||||
begin
|
||||
for var pair in Scope.NameToIndex do
|
||||
begin
|
||||
var name := Scope.NameStrings[pair.Key];
|
||||
if not FSymbols.ContainsKey(name) then
|
||||
FSymbols.Add(name, pair.Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TAstBinder.TUpvalueMapping.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
// Use the custom equality comparer to ensure correct dictionary behavior.
|
||||
Map := TDictionary<TResolvedAddress, Integer>.Create(TResolvedAddressComparer.Create);
|
||||
Nodes := TList<IIdentifierNode>.Create();
|
||||
end;
|
||||
|
||||
destructor TAstBinder.TUpvalueMapping.Destroy;
|
||||
begin
|
||||
Nodes.Free;
|
||||
Map.Free;
|
||||
inherited Destroy;
|
||||
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
|
||||
Result := TAst.TernaryExpr(condition, thenBranch, elseBranch)
|
||||
else
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user