diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj
index 31f0c45..a738789 100644
--- a/ASTPlayground/ASTPlayground.dproj
+++ b/ASTPlayground/ASTPlayground.dproj
@@ -5,9 +5,9 @@
FMX
True
Debug
- Win32
+ Win64
ASTPlayground
- 3
+ 2
Application
ASTPlayground.dpr
@@ -1142,7 +1142,7 @@
- True
+ False
True
diff --git a/ASTPlayground/ASTPlayground.res b/ASTPlayground/ASTPlayground.res
index 29a1295..8e77859 100644
Binary files a/ASTPlayground/ASTPlayground.res and b/ASTPlayground/ASTPlayground.res differ
diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas
index f93d9c8..e1ae840 100644
--- a/ASTPlayground/MainForm.pas
+++ b/ASTPlayground/MainForm.pas
@@ -88,6 +88,7 @@ begin
FWorkspace := TAuraWorkspace.Create(Panel2);
FWorkspace.Parent := Panel2;
FWorkspace.Align := TAlignLayout.Client;
+ FWorkspace.ClipChildren := true;
FWorkspace.OnMouseDown := WorkspaceMouseDown;
diff --git a/ASTPlayground/Myc.Ast.Visualizer.pas b/ASTPlayground/Myc.Ast.Visualizer.pas
index 0575dcb..ff6215e 100644
--- a/ASTPlayground/Myc.Ast.Visualizer.pas
+++ b/ASTPlayground/Myc.Ast.Visualizer.pas
@@ -16,21 +16,33 @@ uses
DraggablePanel;
type
- // Record to store a connection between two pins
+ // ... TPinConnection, TPinShape, TPinAlignment, TAuraWorkspace ...
TPinConnection = record
OutputPin: TControl;
InputPin: TControl;
constructor Create(AOutputPin, AInputPin: TControl);
end;
- // Defines the shape of a pin.
TPinShape = (psCircle, psTriangle);
- // Defines the horizontal alignment of a pin on a node.
TPinAlignment = (paLeft, paRight);
TAuraWorkspace = class;
TAstToAuraNodeVisitor = class(TInterfacedObject, IAstVisitor)
+ const
+ // Pin Visuals
+ cPinSize = 10;
+ cDataPinColor = TAlphaColors.Dodgerblue;
+ cExecPinColor = TAlphaColors.Lightgreen;
+
+ // Node Layout
+ cHorizontalPadding = 25; // Padding inside the node title bar
+ cPinOffsetY = 18; // Vertical distance between pins
+ cVerticalPadding = 20; // General vertical padding inside container nodes
+
+ // Specific Node Heights
+ cDefaultNodeHeight = 45; // Default height for simple nodes like BinaryExpr
+ cIfNodeHeight = 60; // Taller height for If nodes with more pins
private
FWorkspace: TAuraWorkspace;
FParentControl: TControl;
@@ -46,9 +58,15 @@ type
AColor: TAlphaColor;
const ATag: string;
AOffsetY: Single = 0
- ): TControl;
+ ): TShape;
+
+ function CreateInput(AParentNode: TAuraNode; const ACaption: string; AOffsetY: Single = 0): TControl;
+ function CreateOutput(AParentNode: TAuraNode; AOffsetY: Single = 0): TControl;
+ function CreateEntry(AParentNode: TAuraNode; AOffsetY: Single = 0): TControl;
+ function CreateExit(AParentNode: TAuraNode; const ACaption: string; AOffsetY: Single = 0): TControl;
+
function FindPinByTag(AParentNode: TAuraNode; const ATag: string): TControl;
- function ConnectPins(InputPin: TControl; OutputNode: TAuraNode; const Tag: string): TControl;
+ function ConnectData(InputPin: TControl; OutputNode: TAuraNode): TControl;
public
constructor Create(
AWorkspace: TAuraWorkspace;
@@ -148,17 +166,39 @@ begin
FConnections := AConnections;
end;
-function TAstToAuraNodeVisitor.ConnectPins(InputPin: TControl; OutputNode: TAuraNode; const Tag: string): TControl;
+function TAstToAuraNodeVisitor.ConnectData(InputPin: TControl; OutputNode: TAuraNode): TControl;
begin
- Result := FindPinByTag(OutputNode, Tag);
+ Result := FindPinByTag(OutputNode, 'data.out');
if Assigned(Result) then
FConnections.Add(TPinConnection.Create(Result, InputPin));
end;
+function TAstToAuraNodeVisitor.CreateInput(AParentNode: TAuraNode; const ACaption: string; AOffsetY: Single = 0): TControl;
+begin
+ Result := CreatePin(AParentNode, psCircle, paLeft, cDataPinColor, 'data.in.' + ACaption, AOffsetY);
+ Result.Hint := ACaption;
+ Result.ShowHint := ACaption <> '';
+end;
+
+function TAstToAuraNodeVisitor.CreateOutput(AParentNode: TAuraNode; AOffsetY: Single = 0): TControl;
+begin
+ Result := CreatePin(AParentNode, psCircle, paRight, cDataPinColor, 'data.out', AOffsetY);
+end;
+
+function TAstToAuraNodeVisitor.CreateEntry(AParentNode: TAuraNode; AOffsetY: Single = 0): TControl;
+begin
+ Result := CreatePin(AParentNode, psTriangle, paLeft, cExecPinColor, 'exec.in', AOffsetY);
+end;
+
+function TAstToAuraNodeVisitor.CreateExit(AParentNode: TAuraNode; const ACaption: string; AOffsetY: Single = 0): TControl;
+begin
+ var cap := ACaption;
+ if cap <> '' then
+ cap := '.' + cap;
+ Result := CreatePin(AParentNode, psTriangle, paRight, cExecPinColor, 'exec.out' + cap, AOffsetY);
+end;
+
function TAstToAuraNodeVisitor.CreateNodeControl(const ATitle, ADetails: string): TAuraNode;
-const
- // Horizontal padding on each side of the title to ensure text and pins are fully visible.
- cHorizontalPadding = 25;
var
LFullTitle: string;
LTextWidth: Single;
@@ -199,10 +239,8 @@ function TAstToAuraNodeVisitor.CreatePin(
AAlignment: TPinAlignment;
AColor: TAlphaColor;
const ATag: string;
- AOffsetY: Single
-): TControl;
-const
- cPinSize = 10;
+ AOffsetY: Single = 0
+): TShape;
var
LPosX, LPosY: Single;
begin
@@ -240,7 +278,7 @@ begin
// Common properties
Result.Parent := AParentNode;
Result.SetBounds(LPosX, LPosY, cPinSize, cPinSize);
- Result.HitTest := False;
+ Result.HitTest := true;
// Use TagString to identify the control as a pin and describe its function.
Result.TagString := ATag;
@@ -265,9 +303,6 @@ begin
end;
function TAstToAuraNodeVisitor.VisitAssignment(const Node: IAssignmentNode): TAstValue;
-const
- cExecPinColor = TAlphaColors.Lightgreen;
- cDataPinColor = TAlphaColors.Dodgerblue;
var
LAssignmentNode, LValueNode: TAuraNode;
LInputPin: TControl;
@@ -285,12 +320,12 @@ begin
// Create the assignment node and its pins
LAssignmentNode := CreateNodeControl('Assignment', Node.Identifier.Name);
- CreatePin(LAssignmentNode, psTriangle, paLeft, cExecPinColor, 'Pin.Exec.In', -8);
- CreatePin(LAssignmentNode, psTriangle, paRight, cExecPinColor, 'Pin.Exec.Out');
- LInputPin := CreatePin(LAssignmentNode, psCircle, paLeft, cDataPinColor, 'Pin.Data.In', 8);
+ CreateEntry(LAssignmentNode, 0);
+ CreateExit(LAssignmentNode, '', 0);
+ LInputPin := CreateInput(LAssignmentNode, Node.Identifier.Name, 0);
// Connect the value to the assignment
- ConnectPins(LInputPin, LValueNode, 'Pin.Data.Out');
+ ConnectData(LInputPin, LValueNode);
// Finalize visitor state
FCurrentPos.X := LStartPosition.X;
@@ -301,9 +336,6 @@ begin
end;
function TAstToAuraNodeVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue;
-const
- cDataPinColor = TAlphaColors.Dodgerblue;
- cDefaultHeight = 45;
var
LBinaryExprNode, LLeftNode, LRightNode: TAuraNode;
LInputPin1, LInputPin2: TControl;
@@ -329,20 +361,21 @@ begin
// Vertically center the binary expression node between its children
var LTotalChildHeight := LRightEndY - LStartPosition.Y;
var LNodeCenterY := LStartPosition.Y + (LTotalChildHeight / 2);
- FCurrentPos.Y := LNodeCenterY - (cDefaultHeight / 2);
+ FCurrentPos.Y := LNodeCenterY - (cDefaultNodeHeight / 2);
LBinaryExprNode := CreateNodeControl(Node.Operator.ToString, '');
+ LBinaryExprNode.TitleFont.Size := 20;
// Create pins for the binary expression node
- LInputPin1 := CreatePin(LBinaryExprNode, psCircle, paLeft, cDataPinColor, 'Pin.Data.In.1', -8);
- LInputPin2 := CreatePin(LBinaryExprNode, psCircle, paLeft, cDataPinColor, 'Pin.Data.In.2', 8);
- CreatePin(LBinaryExprNode, psCircle, paRight, cDataPinColor, 'Pin.Data.Out', 0);
+ LInputPin1 := CreateInput(LBinaryExprNode, LLeftNode.Name, -8);
+ LInputPin2 := CreateInput(LBinaryExprNode, LRightNode.Name, 8);
+ CreateOutput(LBinaryExprNode, 0);
// Connect the output of the left child to the first input of the binary node
- ConnectPins(LInputPin1, LLeftNode, 'Pin.Data.Out');
+ ConnectData(LInputPin1, LLeftNode);
// Connect the output of the right child to the second input of the binary node
- ConnectPins(LInputPin2, LRightNode, 'Pin.Data.Out');
+ ConnectData(LInputPin2, LRightNode);
// Finalize visitor state for the next node at this level
FCurrentPos.X := LStartPosition.X;
@@ -404,67 +437,109 @@ begin
end;
function TAstToAuraNodeVisitor.VisitConstant(const Node: IConstantNode): TAstValue;
-const
- cDataPinColor = TAlphaColors.Dodgerblue;
var
LConstantNode: TAuraNode;
begin
// A constant has a data output pin.
// Data outputs are round and on the right side of the box.
LConstantNode := CreateNodeControl('Constant', Node.Value.ToString);
- CreatePin(LConstantNode, psCircle, paRight, cDataPinColor, 'Pin.Data.Out', 0);
+ CreateOutput(LConstantNode, 0);
Result := TAstValue.Void;
end;
function TAstToAuraNodeVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
var
- Arg: IExpressionNode;
+ LFuncCallNode: TAuraNode;
+ LInputResultNodes: TList;
+ LInputNode: IExpressionNode;
+ LStartPosition: TPointF;
+ LEndY, LMaxChildWidth: Single;
+ i: Integer;
+ LCalleePin: TControl;
+ LArgPin: array of TControl;
+ LCurrentInputOffsetY, LCurrentOutputOffsetY: Single;
begin
- CreateNodeControl('FunctionCall', '');
- FCurrentPos.X := FCurrentPos.X + FSpacing.X;
+ LStartPosition := FCurrentPos;
+ LInputResultNodes := TList.Create;
try
- // Callee
- CreateNodeControl('Callee', '');
- FCurrentPos.X := FCurrentPos.X + FSpacing.X;
- try
- Node.Callee.Accept(Self);
- finally
- FCurrentPos.X := FCurrentPos.X - FSpacing.X;
+ // 1. Visit all input nodes first (Callee + Arguments) and collect their visual representations.
+ Node.Callee.Accept(Self);
+ LInputResultNodes.Add(FLastNode);
+
+ for LInputNode in Node.Arguments do
+ begin
+ LInputNode.Accept(Self);
+ LInputResultNodes.Add(FLastNode);
+ end;
+ LEndY := FCurrentPos.Y;
+
+ // 2. Determine the layout bounds of the input nodes.
+ LMaxChildWidth := 0;
+ for var LNode in LInputResultNodes do
+ LMaxChildWidth := Max(LMaxChildWidth, LNode.Position.X + LNode.Width);
+
+ // 3. Position and create the 'FunctionCall' node to the right of the inputs.
+ FCurrentPos.X := LMaxChildWidth + FSpacing.X;
+
+ // Calculate dynamic height based on the number of pins
+ var LNumInputPins := 2 + Node.Arguments.Count;
+ var LNumOutputPins := 2;
+ var LMaxPins := Max(LNumInputPins, LNumOutputPins);
+ var LNodeHeight := cVerticalPadding + (LMaxPins - 1) * cPinOffsetY;
+
+ var LTotalInputHeight := LEndY - LStartPosition.Y;
+ var LNodeCenterY := LStartPosition.Y + (LTotalInputHeight / 2);
+ FCurrentPos.Y := LNodeCenterY - (LNodeHeight / 2);
+
+ LFuncCallNode := CreateNodeControl('FunctionCall', '');
+ LFuncCallNode.Height := LNodeHeight;
+
+ // 4. Create all necessary pins on the 'FunctionCall' node with proper spacing.
+ // Left side (Inputs)
+ LCurrentInputOffsetY := -(LNumInputPins - 1) / 2.0 * cPinOffsetY;
+ CreateEntry(LFuncCallNode, LCurrentInputOffsetY);
+ LCurrentInputOffsetY := LCurrentInputOffsetY + cPinOffsetY;
+ LCalleePin := CreateInput(LFuncCallNode, LInputResultNodes[0].Name, LCurrentInputOffsetY);
+ LCurrentInputOffsetY := LCurrentInputOffsetY + cPinOffsetY;
+ SetLength(LArgPin, Node.Arguments.Count);
+ for i := 0 to Node.Arguments.Count - 1 do
+ begin
+ LArgPin[i] := CreateInput(LFuncCallNode, LInputResultNodes[i + 1].Name + i.ToString, LCurrentInputOffsetY);
+ LCurrentInputOffsetY := LCurrentInputOffsetY + cPinOffsetY;
end;
- // Arguments
- if Node.Arguments.Count > 0 then
- begin
- CreateNodeControl('Arguments', '');
- FCurrentPos.X := FCurrentPos.X + FSpacing.X;
- try
- for Arg in Node.Arguments do
- Arg.Accept(Self);
- finally
- FCurrentPos.X := FCurrentPos.X - FSpacing.X;
- end;
- end;
+ // Right side (Outputs)
+ LCurrentOutputOffsetY := -(LNumOutputPins - 1) / 2.0 * cPinOffsetY;
+ CreateExit(LFuncCallNode, '', LCurrentOutputOffsetY);
+ LCurrentOutputOffsetY := LCurrentOutputOffsetY + cPinOffsetY;
+ CreateOutput(LFuncCallNode, LCurrentOutputOffsetY);
+
+ // 5. Connect the inputs.
+ ConnectData(LCalleePin, LInputResultNodes[0]);
+ for i := 0 to Node.Arguments.Count - 1 do
+ ConnectData(LArgPin[i], LInputResultNodes[i + 1]);
+
finally
- FCurrentPos.X := FCurrentPos.X - FSpacing.X;
+ LInputResultNodes.Free;
end;
+
+ // 6. Finalize visitor state.
+ FCurrentPos.X := LStartPosition.X;
+ FCurrentPos.Y := Max(LEndY, LFuncCallNode.Position.Y + LFuncCallNode.Height + FSpacing.Y);
+ FLastNode := LFuncCallNode;
+
Result := TAstValue.Void;
end;
function TAstToAuraNodeVisitor.VisitIdentifier(const Node: IIdentifierNode): TAstValue;
-const
- cDataPinColor = TAlphaColors.Dodgerblue;
begin
CreateNodeControl('Identifier', Node.Name);
// Add a data output pin on the right side.
- CreatePin(FLastNode, psCircle, paRight, cDataPinColor, 'Pin.Data.Out');
+ CreateOutput(FLastNode, 0);
Result := TAstValue.Void;
end;
function TAstToAuraNodeVisitor.VisitIfExpression(const Node: IIfExpressionNode): TAstValue;
-const
- cExecPinColor = TAlphaColors.Lightgreen;
- cDataPinColor = TAlphaColors.Dodgerblue;
- cDefaultHeight = 60; // Taller to accommodate more pins
var
LIfNode, LConditionNode, LThenNode, LElseNode: TAuraNode;
LConditionInputPin, LThenOutputPin, LElseOutputPin: TControl;
@@ -481,20 +556,20 @@ begin
// Position and create the 'If' node to the right of the condition's subtree.
LMaxChildWidth := LConditionNode.Position.X + LConditionNode.Width;
FCurrentPos.X := LMaxChildWidth + FSpacing.X;
- FCurrentPos.Y := LStartPosition.Y + (LConditionNode.Position.Y + LConditionNode.Height / 2) - LStartPosition.Y - (cDefaultHeight / 2);
+ FCurrentPos.Y := LStartPosition.Y + (LConditionNode.Position.Y + LConditionNode.Height / 2) - LStartPosition.Y - (cIfNodeHeight / 2);
LIfNode := CreateNodeControl('If', '');
- LIfNode.Height := cDefaultHeight;
- CreatePin(LIfNode, psTriangle, paLeft, cExecPinColor, 'Pin.Exec.In', -12);
- LConditionInputPin := CreatePin(LIfNode, psCircle, paLeft, cDataPinColor, 'Pin.Data.In.Condition', 12);
- LThenOutputPin := CreatePin(LIfNode, psTriangle, paRight, cExecPinColor, 'Pin.Exec.Out.Then', -12);
+ LIfNode.Height := cIfNodeHeight;
+ CreateEntry(LIfNode, -12);
+ LConditionInputPin := CreateInput(LIfNode, 'Condition', 12);
+ LThenOutputPin := CreateExit(LIfNode, 'Then', -12);
if Assigned(Node.ElseBranch) then
- LElseOutputPin := CreatePin(LIfNode, psTriangle, paRight, cExecPinColor, 'Pin.Exec.Out.Else', 12)
+ LElseOutputPin := CreateExit(LIfNode, 'Else', 12)
else
LElseOutputPin := nil;
// Connect the condition's output to the 'If' node's data input.
- ConnectPins(LConditionInputPin, LConditionNode, 'Pin.Data.Out');
+ ConnectData(LConditionInputPin, LConditionNode);
// Visit the 'Then' and 'Else' execution branches, placing them to the right of the 'If' node.
var LBranchStartX := LIfNode.Position.X + LIfNode.Width + FSpacing.X;
@@ -505,7 +580,7 @@ begin
Node.ThenBranch.Accept(Self);
LThenNode := FLastNode;
LThenEndY := FCurrentPos.Y;
- // ConnectPins(FindPinByTag(LThenNode, 'Pin.Exec.In'), LIfNode, 'Pin.Exec.Out.Then');
+ // ConnectData(FindPinByTag(LThenNode, 'Pin.Exec.In'), LIfNode, 'Pin.Exec.Out.Then');
// Else branch (if it exists)
LElseEndY := LThenEndY;
@@ -516,7 +591,7 @@ begin
Node.ElseBranch.Accept(Self);
LElseNode := FLastNode;
LElseEndY := FCurrentPos.Y;
- // ConnectPins(FindPinByTag(LElseNode, 'Pin.Exec.In'), LIfNode, 'Pin.Exec.Out.Else');
+ // ConnectData(FindPinByTag(LElseNode, 'Pin.Exec.In'), LIfNode, 'Pin.Exec.Out.Else');
end;
// Finalize visitor state.
@@ -528,9 +603,6 @@ begin
end;
function TAstToAuraNodeVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
-const
- cExecPinColor = TAlphaColors.Lightgreen;
- cDataPinColor = TAlphaColors.Dodgerblue;
var
ParamStr: String;
LLambdaNode: TAuraNode;
@@ -578,7 +650,7 @@ begin
LLambdaNode.Height := Max(LLambdaNode.Height, LMaxBottom + FSpacing.Y);
// Add a data output pin for the resulting closure.
- CreatePin(LLambdaNode, psCircle, paRight, cDataPinColor, 'Pin.Data.Out');
+ CreateOutput(LLambdaNode, 0);
// Update the current Y position of the main visitor to be below the now correctly sized lambda node.
FCurrentPos.Y := LLambdaNode.Position.Y + LLambdaNode.Height + FSpacing.Y;
@@ -599,10 +671,6 @@ begin
end;
function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
-const
- cExecPinColor = TAlphaColors.Lightgreen;
- cDataPinColor = TAlphaColors.Dodgerblue;
- cInitializerTreeWidth = 150;
var
LVarDeclNode, LInitializerNode: TAuraNode;
LStartPosition: TPointF;
@@ -619,20 +687,27 @@ begin
LInitializerNode := FLastNode;
LInitializerEndY := FCurrentPos.Y;
- FCurrentPos.X := LStartPosition.X + cInitializerTreeWidth + FSpacing.X;
+
+ // Position the VarDecl node dynamically to the right of the initializer's visual tree.
+ FCurrentPos.X := LInitializerNode.Position.X + LInitializerNode.Width + FSpacing.X;
FCurrentPos.Y := LStartPosition.Y;
end;
LVarDeclNode := CreateNodeControl('VarDecl', Node.Identifier.Name);
LVarDeclNodeEndY := FCurrentPos.Y;
- CreatePin(LVarDeclNode, psTriangle, paLeft, cExecPinColor, 'Pin.Exec.In', -8);
- CreatePin(LVarDeclNode, psTriangle, paRight, cExecPinColor, 'Pin.Exec.Out', -8);
+ CreateEntry(LVarDeclNode, -8);
+ CreateExit(LVarDeclNode, '', -8);
if Assigned(Node.Initializer) then
begin
- LInputPin := CreatePin(LVarDeclNode, psCircle, paLeft, cDataPinColor, 'Pin.Data.In', 8);
- ConnectPins(LInputPin, LInitializerNode, 'Pin.Data.Out');
+ LInputPin := CreateInput(LVarDeclNode, LInitializerNode.Name, 8);
+ ConnectData(LInputPin, LInitializerNode);
+
+ // Vertically center the VarDecl node relative to the initializer's visual tree.
+ var LInitializerHeight := LInitializerEndY - LStartPosition.Y;
+ LVarDeclNode.Position.Y := LStartPosition.Y + (LInitializerHeight / 2) - (LVarDeclNode.Height / 2);
+ LVarDeclNodeEndY := LVarDeclNode.Position.Y + LVarDeclNode.Height + FSpacing.Y;
FCurrentPos.Y := Max(LInitializerEndY, LVarDeclNodeEndY);
FCurrentPos.X := LStartPosition.X;
@@ -693,7 +768,7 @@ begin
LPath := TPathData.Create;
try
LDeltaX := LEndPoint.X - LStartPoint.X;
- LControlOffset := Max(50, Abs(LDeltaX) / 2);
+ LControlOffset := Abs(LDeltaX) / 2;
LControlPoint1 := TPointF.Create(LStartPoint.X + LControlOffset, LStartPoint.Y);
LControlPoint2 := TPointF.Create(LEndPoint.X - LControlOffset, LEndPoint.Y);
diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas
index ec362cf..1ea3340 100644
--- a/Src/AST/Myc.Ast.Evaluator.pas
+++ b/Src/AST/Myc.Ast.Evaluator.pas
@@ -273,9 +273,9 @@ begin
end;
if boolResult then
- Result := TAstValue.FromScalar(TScalar.FromInt64(1))
+ Result := TAstValue.FromScalar(TScalar.FromBoolean(true))
else
- Result := TAstValue.FromScalar(TScalar.FromInt64(0));
+ Result := TAstValue.FromScalar(TScalar.FromBoolean(false));
end;
function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue;
diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas
index b900dfd..9de58c1 100644
--- a/Src/AST/Myc.Ast.Nodes.pas
+++ b/Src/AST/Myc.Ast.Nodes.pas
@@ -313,7 +313,7 @@ begin
case Self of
boAdd: Result := '+';
boSubtract: Result := '-';
- boMultiply: Result := '*';
+ boMultiply: Result := #$2A2F;
boDivide: Result := '/';
boEqual: Result := '==';
boNotEqual: Result := '!=';