Files
MycLib/Src/AST/Myc.Ast.Lowering.pas
T
2025-11-01 15:44:56 +01:00

154 lines
4.8 KiB
ObjectPascal

unit Myc.Ast.Lowering;
interface
uses
System.SysUtils,
System.Classes,
System.Generics.Collections,
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Ast.Nodes,
Myc.Ast.Visitor,
Myc.Ast.Scope,
Myc.Ast.Types,
Myc.Ast,
Myc.Ast.Binding.Nodes;
type
IAstLowerer = interface(IAstVisitor)
function Execute(const RootNode: IAstNode): IAstNode;
end;
// This transformer runs *after* TypeChecker (Phase 3).
// It "lowers" the AST by rewriting complex nodes into simpler ones
// that the evaluator can understand (e.g., operator folding).
TAstLowerer = class(TAstTransformer, IAstLowerer)
private
FBinaryOperators: TDictionary<string, TScalar.TBinaryOp>;
FUnaryOperators: TDictionary<string, TScalar.TUnaryOp>;
function SetType(const Node: IAstNode; const AType: IStaticType): IAstNode;
protected
// Override to find nodes to lower
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
public
constructor Create;
destructor Destroy; override;
function Execute(const RootNode: IAstNode): IAstNode;
class function Lower(const RootNode: IAstNode): IAstNode; static;
end;
implementation
uses
System.Generics.Defaults,
Myc.Data.Keyword;
{ TAstLowerer }
constructor TAstLowerer.Create;
var
op: TScalar.TBinaryOp;
begin
inherited Create;
// Operator folding maps
FBinaryOperators := TDictionary<string, TScalar.TBinaryOp>.Create;
for op := Low(TScalar.TBinaryOp) to High(TScalar.TBinaryOp) do
FBinaryOperators.Add(op.ToString, op);
FUnaryOperators := TDictionary<string, TScalar.TUnaryOp>.Create;
FUnaryOperators.Add('not', TScalar.TUnaryOp.Not);
// Note: '-' is handled as a special case in VisitFunctionCall
end;
destructor TAstLowerer.Destroy;
begin
FUnaryOperators.Free;
FBinaryOperators.Free;
inherited;
end;
class function TAstLowerer.Lower(const RootNode: IAstNode): IAstNode;
begin
var lowerer := TAstLowerer.Create as IAstLowerer;
Result := lowerer.Execute(RootNode);
end;
function TAstLowerer.Execute(const RootNode: IAstNode): IAstNode;
begin
var transformedValue := Accept(RootNode);
if transformedValue.IsVoid then
Result := TAst.Block([])
else
Result := transformedValue.AsIntf<IAstNode>;
if Assigned(Result) then
(Result as TAstNode).StaticType := (Result as TAstNode).StaticType;
end;
function TAstLowerer.SetType(const Node: IAstNode; const AType: IStaticType): IAstNode;
begin
(Node as TAstNode).StaticType := AType;
Result := Node;
end;
function TAstLowerer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
calleeIdentifier: TIdentifierNode;
binaryOp: TScalar.TBinaryOp;
unaryOp: TScalar.TUnaryOp;
left, right: IAstNode;
begin
// --- Transformation: Keyword-as-Function ---
// (This logic is correctly handled in TAstBinder)
if (Node.Callee is TKeywordNode) then
begin
// This node should have been transformed by the TAstBinder.
exit(inherited VisitFunctionCall(Node));
end;
// --- Optimization: Operator Folding ---
if (Node.Callee is TIdentifierNode) then
begin
calleeIdentifier := Node.Callee as TIdentifierNode;
if (Length(Node.Arguments) = 2) then
begin
if FBinaryOperators.TryGetValue(calleeIdentifier.Name, binaryOp) then
begin
left := Accept(Node.Arguments[0]).AsIntf<IAstNode>;
right := Accept(Node.Arguments[1]).AsIntf<IAstNode>;
var binExpr := TAst.BinaryExpr(left, binaryOp, right);
Result := TDataValue.FromIntf<IAstNode>(SetType(binExpr, (Node as TAstNode).StaticType));
exit;
end;
end;
if (Length(Node.Arguments) = 1) then
begin
if FUnaryOperators.TryGetValue(calleeIdentifier.Name, unaryOp) then
begin
right := Accept(Node.Arguments[0]).AsIntf<IAstNode>;
var unExpr := TAst.UnaryExpr(unaryOp, right);
Result := TDataValue.FromIntf<IAstNode>(SetType(unExpr, (Node as TAstNode).StaticType));
exit;
end;
if (calleeIdentifier.Name = '-') then
begin
right := Accept(Node.Arguments[0]).AsIntf<IAstNode>;
var unExpr := TAst.UnaryExpr(TScalar.TUnaryOp.Negate, right);
Result := TDataValue.FromIntf<IAstNode>(SetType(unExpr, (Node as TAstNode).StaticType));
exit;
end;
end;
end;
// If no rewrite matched, continue traversal
Result := inherited VisitFunctionCall(Node);
end;
end.