Files
MycLib/Src/AST/Myc.Ast.Scope.pas
T
Michael Schimmel fa9328a183 AST refactoring
2025-08-29 09:44:17 +02:00

124 lines
3.4 KiB
ObjectPascal

unit Myc.Ast.Scope;
interface
uses
System.SysUtils,
System.Generics.Collections,
System.Classes,
Myc.Ast.Nodes;
type
T_ExecutionScope = class(TInterfacedObject, IExecutionScope)
private
FParent: IExecutionScope;
FVariables: TDictionary<string, TAstValue>;
procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
protected
// IExecutionScope
function GetParent: IExecutionScope;
procedure Clear;
function FindValue(const Name: string; out Value: TAstValue): Boolean;
procedure SetValue(const Name: string; const Value: TAstValue);
procedure AssignValue(const Name: string; const Value: TAstValue);
function Dump: string;
public
constructor Create(AParent: IExecutionScope = nil);
destructor Destroy; override;
end;
implementation
{ T_ExecutionScope }
constructor T_ExecutionScope.Create(AParent: IExecutionScope);
begin
inherited Create;
FParent := AParent;
FVariables := TDictionary<string, TAstValue>.Create;
end;
destructor T_ExecutionScope.Destroy;
begin
FVariables.Free;
inherited Destroy;
end;
function T_ExecutionScope.GetParent: IExecutionScope;
begin
Result := FParent;
end;
procedure T_ExecutionScope.AssignValue(const Name: string; const Value: TAstValue);
begin
if FVariables.ContainsKey(Name) then
FVariables.AddOrSetValue(Name, Value)
else if Assigned(FParent) then
FParent.AssignValue(Name, Value)
else
raise Exception.CreateFmt('Cannot assign to undeclared variable "%s".', [Name]);
end;
procedure T_ExecutionScope.Clear;
begin
FVariables.Clear;
if Assigned(FParent) then
FParent.Clear;
end;
procedure T_ExecutionScope.DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
var
pair: TPair<string, TAstValue>;
indentStr: string;
begin
indentStr := ''.PadLeft(AIndent);
if (FVariables.Count > 0) then
begin
for pair in FVariables do
ABuilder.AppendLine(indentStr + Format(' %s: %s', [pair.Key, pair.Value.ToString]));
end
else
begin
ABuilder.AppendLine(indentStr + ' (empty)');
end;
if Assigned(FParent) then
begin
ABuilder.AppendLine(indentStr + '[Parent Scope]');
// As FParent is now an interface, we must cast it back to the class to call the private DumpScope.
// This indicates that DumpScope should perhaps be part of the interface or handled differently.
// For now, we use a cast to keep the functionality.
(FParent as T_ExecutionScope).DumpScope(ABuilder, AIndent + 2);
end;
end;
function T_ExecutionScope.Dump: string;
var
builder: TStringBuilder;
begin
builder := TStringBuilder.Create;
try
builder.AppendLine('[Current Scope]');
DumpScope(builder, 0);
Result := builder.ToString.TrimRight;
finally
builder.Free;
end;
end;
function T_ExecutionScope.FindValue(const Name: string; out Value: TAstValue): Boolean;
begin
Result := FVariables.TryGetValue(Name, Value);
if not Result and Assigned(FParent) then
begin
Result := FParent.FindValue(Name, Value);
end;
end;
procedure T_ExecutionScope.SetValue(const Name: string; const Value: TAstValue);
begin
FVariables.AddOrSetValue(Name, Value);
end;
end.