diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index 6e197a4..fe3c821 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -306,7 +306,27 @@ begin smaScope := smaDescriptor.CreateScope(Scope); smaVisitor := CreateEvaluator(smaScope); - Scope.Define('CreateSMA', smaVisitor.Execute(boundSmaAst)); + Scope.Define( + 'print', + TDataValue( + function(const Args: TArray): TDataValue + begin + var str := TStringBuilder.Create; + try + for var i := 0 to High(Args) do + begin + if Args[i].Kind = vkText then + str.Append(Args[i].AsText) + else + str.Append(Args[i].ToString); + end; + Memo1.Lines.Add(str.ToString); + finally + str.Free; + end; + end + ) + ); end ); diff --git a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas index 127007a..2c38674 100644 --- a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas +++ b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas @@ -149,7 +149,6 @@ type // If true, the node is drawn without a border and with a transparent background. FFrameless: Boolean; FVisualizer: IAstVisualizer; - function AddLabel(const Txt: String): TLabel; procedure SetBackgroundColor(const Value: TAlphaColor); procedure SetBorderColor(const Value: TAlphaColor); procedure SetBorderWidth(const Value: Single); @@ -162,6 +161,11 @@ type procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override; property Visualizer: IAstVisualizer read FVisualizer; procedure SetupNode; virtual; + + function AddLabel(Parent: TControl; const Txt: String): TLabel; + function AddContainer(Parent: TControl; Orientation: TLayoutOrientation; Alignment: TLayoutAlignment): TAutoFitControl; + function AddExpr(Parent: TControl; const Title: String; const Node: IAstNode): TAuraNode; + public constructor Create(const AVisualizer: IAstVisualizer); reintroduce; destructor Destroy; override; @@ -450,7 +454,6 @@ type private FNode: IMemberAccessNode; FBaseNode: TAuraNode; - // FMemberNode is handled by a label, store Base only protected procedure SetupNode; override; public @@ -930,10 +933,29 @@ begin inherited; end; -function TAuraNode.AddLabel(const Txt: String): TLabel; +function TAuraNode.AddContainer(Parent: TControl; Orientation: TLayoutOrientation; Alignment: TLayoutAlignment): TAutoFitControl; +begin + Result := TAutoFitControl.Create(Self); + Result.Parent := Parent; + Result.Orientation := Orientation; + Result.Alignment := Alignment; + Result.HitTest := False; // Pass clicks to parent +end; + +function TAuraNode.AddExpr(Parent: TControl; const Title: String; const Node: IAstNode): TAuraNode; +begin + var cont := AddContainer(Parent, loHorizontal, laCenter); + + var lbl := AddLabel(cont, Title); + lbl.Font.Style := lbl.Font.Style + [TFontStyle.fsBold]; + + Result := FVisualizer.Clone(cont, FVisualizer.ExprDepth + 1).CallAccept(Node); +end; + +function TAuraNode.AddLabel(Parent: TControl; const Txt: String): TLabel; begin Result := TLabel.Create(Self); // Owner is the node itself - Result.Parent := Self; + Result.Parent := Parent; Result.Position.Point := TPoint.Create(cNodePadding, cNodePadding); Result.Margins.Left := cNodePadding; @@ -1134,7 +1156,7 @@ begin begin // Display value directly in a centered label, frameless node Frameless := true; - AddLabel(valueStr); + AddLabel(Self, valueStr); end else begin @@ -1142,11 +1164,11 @@ begin Frameless := False; Orientation := loVertical; - var titleLabel := AddLabel('Const'); + var titleLabel := AddLabel(Self, 'Const'); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; // Value Label - var valueLabel := AddLabel(valueStr); + var valueLabel := AddLabel(Self, valueStr); valueLabel.AutoSize := False; // ***Important: Disable AutoSize when Align=Client*** valueLabel.WordWrap := True; // Allow wrapping for long text end; @@ -1173,7 +1195,7 @@ begin BeginUpdate; try Frameless := true; // Identifiers are frameless - AddLabel(FNode.Name); // Add label with the identifier name + AddLabel(Self, FNode.Name); // Add label with the identifier name finally EndUpdate; end; @@ -1214,7 +1236,7 @@ begin visu.CallAccept(FNode.Left); // 2. Operator Label - AddLabel(FNode.Operator.ToString); + AddLabel(Self, FNode.Operator.ToString); // 3. RightNode Expression Node (Visited Recursively) visu.CallAccept(FNode.Right); @@ -1282,15 +1304,10 @@ begin if i = High(FNode.Expressions) then begin // Last expression: create a horizontal container for "return [expr]" - returnContainer := TAutoFitControl.Create(Self); - returnContainer.Parent := Self; // Parent is the main block node - returnContainer.Orientation := loHorizontal; - returnContainer.Alignment := laCenter; // Center "return" label vertically next to the expression - returnContainer.HitTest := False; // Pass clicks through + returnContainer := AddContainer(Self, loHorizontal, laCenter); // Add "return" label to the container - titleLabel := AddLabel('return'); // Use AddLabel from TAuraNode (Self) - titleLabel.Parent := returnContainer; // Set parent to the new container + titleLabel := AddLabel(returnContainer, 'return'); // Use AddLabel from TAuraNode (Self) titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; // Create a new visualizer targeting this container @@ -1339,7 +1356,7 @@ begin Orientation := loVertical; // Title - titleLabel := AddLabel('Unary Op: ' + FNode.Operator.ToString); + titleLabel := AddLabel(Self, 'Unary Op: ' + FNode.Operator.ToString); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; // Child @@ -1373,24 +1390,18 @@ end; procedure TAuraIfExpressionNode.SetupNode; var visu: IAstVisualizer; - titleLabel: TLabel; + lbl: TLabel; begin - visu := FVisualizer.Clone(Self, FVisualizer.ExprDepth + 1); - BeginUpdate; try Frameless := False; Orientation := loVertical; + Alignment := laFlush; - titleLabel := AddLabel('if'); - titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; - - // Add children - FConditionNode := visu.CallAccept(FNode.Condition); - FThenNode := visu.CallAccept(FNode.ThenBranch); + FConditionNode := AddExpr(Self, 'if', FNode.Condition); + FThenNode := AddExpr(Self, 'then', FNode.ThenBranch); if Assigned(FNode.ElseBranch) then - FElseNode := visu.CallAccept(FNode.ElseBranch); - + FElseNode := AddExpr(Self, 'else', FNode.ElseBranch); finally EndUpdate; end; @@ -1421,9 +1432,9 @@ begin Orientation := loHorizontal; FConditionNode := visu.CallAccept(FNode.Condition); - AddLabel('?'); + AddLabel(Self, '?'); FThenNode := visu.CallAccept(FNode.ThenBranch); - AddLabel(':'); + AddLabel(Self, ':'); FElseNode := visu.CallAccept(FNode.ElseBranch); finally EndUpdate; @@ -1475,36 +1486,33 @@ begin BackgroundColor := $090000ff; // Create a horizontal container for the title/params - titleContainer := TAutoFitControl.Create(Self); - titleContainer.Parent := Self; - titleContainer.Orientation := loHorizontal; - titleContainer.HitTest := False; // Pass clicks to parent + titleContainer := AddContainer(Self, loHorizontal, laCenter); // 1. Lambda Symbol Label (Title) - paramLabel := AddLabel(WideChar($03BB)); // Lambda + paramLabel := AddLabel(Self, WideChar($03BB)); // Lambda paramLabel.Parent := titleContainer; // Add to title container paramLabel.Font.Style := paramLabel.Font.Style + [TFontStyle.fsBold]; // 2. Add Parameters - paramLabel := AddLabel('('); + paramLabel := AddLabel(Self, '('); paramLabel.Parent := titleContainer; FParamLabels.Add(paramLabel); for i := 0 to High(FNode.Parameters) do begin - paramLabel := AddLabel(FNode.Parameters[i].Name); + paramLabel := AddLabel(Self, FNode.Parameters[i].Name); paramLabel.Parent := titleContainer; FParamLabels.Add(paramLabel); if i < High(FNode.Parameters) then begin - paramLabel := AddLabel(','); + paramLabel := AddLabel(Self, ','); paramLabel.Parent := titleContainer; FParamLabels.Add(paramLabel); end; end; - paramLabel := AddLabel(')'); + paramLabel := AddLabel(Self, ')'); paramLabel.Parent := titleContainer; FParamLabels.Add(paramLabel); @@ -1557,7 +1565,7 @@ begin Frameless := True; // Horizontal layout Orientation := loHorizontal; - var callLabel := AddLabel('call '); + var callLabel := AddLabel(Self, 'call '); callLabel.Font.Style := callLabel.Font.Style + [TFontStyle.fsBold]; // Callee @@ -1566,7 +1574,7 @@ begin // Arguments if Length(FNode.Arguments) > 0 then begin - AddLabel('('); + AddLabel(Self, '('); for i := 0 to High(FNode.Arguments) do begin @@ -1574,10 +1582,10 @@ begin FArgumentNodes.Add(argNode); if i < High(FNode.Arguments) then - AddLabel(','); + AddLabel(Self, ','); end; - AddLabel(')'); + AddLabel(Self, ')'); end; finally EndUpdate; @@ -1611,7 +1619,7 @@ begin Frameless := False; Orientation := loVertical; - titleLabel := AddLabel('Macro Expansion'); + titleLabel := AddLabel(Self, 'Macro Expansion'); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; FExpandedBodyNode := visu.CallAccept(FNode.ExpandedBody); @@ -1662,11 +1670,11 @@ begin Orientation := loHorizontal; // 1. 'recur' keyword - titleLabel := AddLabel(WideChar($03BB)); + titleLabel := AddLabel(Self, WideChar($03BB)); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; // 2. Arguments - AddLabel('('); + AddLabel(Self, '('); for i := 0 to High(FNode.Arguments) do begin @@ -1674,10 +1682,10 @@ begin FArgumentNodes.Add(argNode); if i < High(FNode.Arguments) then - AddLabel(','); + AddLabel(Self, ','); end; - AddLabel(')'); + AddLabel(Self, ')'); finally EndUpdate; end; @@ -1717,15 +1725,15 @@ begin Frameless := True; // Horizontal layout Orientation := loHorizontal; - varLabel := AddLabel('var'); + varLabel := AddLabel(Self, 'var'); varLabel.Font.Style := varLabel.Font.Style + [TFontStyle.fsBold]; // Add identifier name as a label - AddLabel(FNode.Identifier.Name); + AddLabel(Self, FNode.Identifier.Name); if Assigned(FNode.Initializer) then begin - AddLabel(':='); + AddLabel(Self, ':='); FInitializerNode := visu.CallAccept(FNode.Initializer); end; finally @@ -1759,9 +1767,9 @@ begin Orientation := loHorizontal; // Add identifier name as a label - AddLabel(FNode.Identifier.Name); + AddLabel(Self, FNode.Identifier.Name); - AddLabel(':='); + AddLabel(Self, ':='); FValueNode := visu.CallAccept(FNode.Value); finally @@ -1811,7 +1819,7 @@ begin paramStr := paramStr + FNode.Parameters[i].Name; end; - titleLabel := AddLabel('Macro Def: ' + FNode.Name.Name + ' (' + paramStr + ')'); + titleLabel := AddLabel(Self, 'Macro Def: ' + FNode.Name.Name + ' (' + paramStr + ')'); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; FBodyNode := visu.CallAccept(FNode.Body); @@ -1843,7 +1851,7 @@ begin try Frameless := False; Orientation := loVertical; - titleLabel := AddLabel('Quasiquote'); + titleLabel := AddLabel(Self, 'Quasiquote'); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; FExpressionNode := visu.CallAccept(FNode.Expression); finally @@ -1874,13 +1882,13 @@ begin Frameless := True; // Horizontal layout Orientation := loHorizontal; - var leftBracket := AddLabel('<'); + var leftBracket := AddLabel(Self, '<'); leftBracket.Padding.Right := 0; leftBracket.Margins.Right := 0; visu.CallAccept(FNode.Expression); - var rightBracket := AddLabel('>'); + var rightBracket := AddLabel(Self, '>'); rightBracket.Padding.Left := 0; rightBracket.Margins.Left := 0; finally @@ -1911,7 +1919,7 @@ begin try Frameless := False; Orientation := loVertical; - titleLabel := AddLabel('Unquote-Splicing'); + titleLabel := AddLabel(Self, 'Unquote-Splicing'); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; FExpressionNode := visu.CallAccept(FNode.Expression); finally @@ -1943,9 +1951,9 @@ begin Orientation := loHorizontal; FBaseNode := visu.CallAccept(FNode.Base); - AddLabel('['); + AddLabel(Self, '['); FIndexNode := visu.CallAccept(FNode.Index); - AddLabel(']'); + AddLabel(Self, ']'); finally EndUpdate; end; @@ -1976,10 +1984,10 @@ begin Orientation := loHorizontal; FBaseNode := visu.CallAccept(FNode.Base); - AddLabel('.'); + AddLabel(Self, '.'); // Add member name as a label - AddLabel(FNode.Member.Name); + AddLabel(Self, FNode.Member.Name); finally EndUpdate; end; @@ -2006,7 +2014,7 @@ begin try Frameless := False; // Leaf node, but framed Orientation := loVertical; - titleLabel := AddLabel('Create Series: ' + FNode.Definition); + titleLabel := AddLabel(Self, 'Create Series: ' + FNode.Definition); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; finally EndUpdate; @@ -2045,7 +2053,7 @@ begin Frameless := False; Orientation := loVertical; - titleLabel := AddLabel('Add Item to: ' + FNode.Series.Name); + titleLabel := AddLabel(Self, 'Add Item to: ' + FNode.Series.Name); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; FValueNode := visu.CallAccept(FNode.Value); @@ -2077,7 +2085,7 @@ begin try Frameless := False; // Leaf node, but framed Orientation := loVertical; - titleLabel := AddLabel('Length of: ' + FNode.Series.Name); + titleLabel := AddLabel(Self, 'Length of: ' + FNode.Series.Name); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; finally EndUpdate; diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index dccead8..b05a105 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -810,14 +810,17 @@ begin condition := Accept(Node.Condition).AsIntf; FNextIsTail := isContextTail; thenBranch := Accept(Node.ThenBranch).AsIntf; - elseBranch := Accept(Node.ElseBranch).AsIntf; + if Assigned(Node.ElseBranch) then + elseBranch := Accept(Node.ElseBranch).AsIntf; conditionType := (condition as TAstNode).StaticType; if not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then raise ETypeException.CreateFmt('If condition must be Ordinal, but got %s', [conditionType.ToString]); thenType := (thenBranch as TAstNode).StaticType; - elseType := (elseBranch as TAstNode).StaticType; + elseType := + if elseBranch <> nil then (elseBranch as TAstNode).StaticType + else TTypes.Void; resultType := TTypeRules.Promote(thenType, elseType); if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then diff --git a/Src/AST/Myc.Ast.Types.pas b/Src/AST/Myc.Ast.Types.pas index 9ed2983..6c3273b 100644 --- a/Src/AST/Myc.Ast.Types.pas +++ b/Src/AST/Myc.Ast.Types.pas @@ -477,6 +477,10 @@ begin if B.Kind = stUnknown then exit(A); + // all operations involving void result void + if (A.Kind = stVoid) or (B.Kind = stVoid) then + exit(TTypes.Void); + // Standard Numeric promotion rule: Float wins if (A.Kind = stFloat) and (B.Kind = stFloat) then exit(TTypes.Float); diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 527c605..676a59b 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -922,13 +922,7 @@ end; class function TAst.IfExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): IIfExpressionNode; begin - Result := - TIfExpressionNode.Create( - ACondition, - AThenBranch, - if AElseBranch = nil then TBlockExpressionNode.Create([]) as IAstNode - else AElseBranch - ); + Result := TIfExpressionNode.Create(ACondition, AThenBranch, AElseBranch); end; class function TAst.Indexer(const ABase: IAstNode; const AIndex: IAstNode): IIndexerNode;