@@ -483,16 +483,16 @@ XTree_Raise = class(XTree_Node)
483483 end ;
484484
485485 XTree_Try = class (XTree_Node)
486- TryBody: XTree_ExprList;
487- Handlers: TExceptionHandlerArray;
488- ElseBody: XTree_Node; // For the final optional 'except' (catch-all)
489-
490- constructor Create(ATryBody: XTree_ExprList; AHandlers: TExceptionHandlerArray; AElseBody: XTree_Node; ACTX: TCompilerContext; DocPos: TDocPos); virtual ; reintroduce;
491- function ToString (offset: string = ' ' ): string; override;
492- function Compile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar; override;
493- function DelayedCompile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar; override;
494- function Copy (): XTree_Node; override;
495- end ;
486+ TryBody: XTree_ExprList;
487+ Handlers: TExceptionHandlerArray;
488+ ElseBody: XTree_Node; // For the final optional 'except' (catch-all)
489+
490+ constructor Create(ATryBody: XTree_ExprList; AHandlers: TExceptionHandlerArray; AElseBody: XTree_Node; ACTX: TCompilerContext; DocPos: TDocPos); virtual ; reintroduce;
491+ function ToString (offset: string = ' ' ): string; override;
492+ function Compile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar; override;
493+ function DelayedCompile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar; override;
494+ function Copy (): XTree_Node; override;
495+ end ;
496496
497497 (* for loop *)
498498 XTree_For = class (XTree_Node)
@@ -506,6 +506,22 @@ XTree_For = class(XTree_Node)
506506 function Copy (): XTree_Node; override;
507507 end ;
508508
509+ (* for..in loop *)
510+ XTree_ForIn = class (XTree_Node)
511+ ItemVar: XTree_Node;
512+ Collection: XTree_Node;
513+ Body: XTree_ExprList;
514+ DeclareIdent: Byte;
515+
516+ constructor Create(AItemVar: XTree_Node; ACollection: XTree_Node; ADeclareIdent: Byte; ABody: XTree_ExprList; ACTX: TCompilerContext; DocPos: TDocPos); virtual ; reintroduce;
517+ function ToString (offset:string=' ' ): string; override;
518+
519+ function Compile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar; override;
520+ function DelayedCompile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar; override;
521+ function Copy (): XTree_Node; override;
522+ end ;
523+
524+
509525 (* Pascal-style repeat-until loop *)
510526 XTree_Repeat = class (XTree_Node)
511527 Condition: XTree_Node;
@@ -4228,7 +4244,6 @@ ctx.RaiseException('No exception handlers are implemented, declare `type Exc
42284244 if Self.ElseBody = nil then
42294245 begin
42304246 Self.ElseBody := XTree_ExprList.Create([
4231- XTree_Print.create([XTree_Int.Create(' 12345' , FContext, FDocPos)], FContext, FDocPos),
42324247 XTree_Raise.Create(XTree_VarStub.Create(ExceptionTempVar,ctx, FDocPos), ctx, Fdocpos)
42334248 ], ctx, Fdocpos);
42344249 end ;
@@ -4396,6 +4411,216 @@ function XTree_For.DelayedCompile(Dest: TXprVar; Flags: TCompilerFlags): TXprVar
43964411
43974412
43984413
4414+ // ============================================================================
4415+ // FOR-ITEM-IN-ARRAY loop
4416+ //
4417+ constructor XTree_ForIn.Create(AItemVar: XTree_Node; ACollection: XTree_Node; ADeclareIdent: Byte; ABody: XTree_ExprList; ACTX: TCompilerContext; DocPos: TDocPos);
4418+ begin
4419+ Self.FContext := ACTX;
4420+ Self.FDocPos := DocPos;
4421+ Self.ItemVar := AItemVar;
4422+ Self.Collection := ACollection;
4423+ Self.DeclareIdent:=ADeclareIdent;
4424+ Self.Body := ABody;
4425+ end ;
4426+
4427+ function XTree_ForIn.ToString (offset: string): string;
4428+ begin
4429+ Result := offset + _AQUA_+' ForIn' +_WHITE_+' (' + LineEnding;
4430+ Result += Self.ItemVar.ToString(offset+' ' ) + ' in ' + Self.Collection.ToString(' ' ) + ' ,' + LineEnding;
4431+ Result += Self.Body.ToString(Offset+' ' ) + LineEnding;
4432+ Result += offset + ' )' ;
4433+ end ;
4434+
4435+ function XTree_ForIn.DelayedCompile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar;
4436+ begin
4437+ // A for-in loop itself has no delayed logic, but its children might.
4438+ Self.Collection.DelayedCompile(Dest, Flags);
4439+ Self.Body.DelayedCompile(Dest, Flags);
4440+ Result := NullResVar;
4441+ end ;
4442+
4443+ function XTree_ForIn.Copy (): XTree_Node;
4444+ begin
4445+ Result := XTree_ForIn.Create(
4446+ Self.ItemVar.Copy as XTree_Identifier,
4447+ Self.Collection.Copy,
4448+ Self.DeclareIdent,
4449+ Self.Body.Copy as XTree_ExprList,
4450+ FContext,
4451+ FDocPos
4452+ );
4453+ end ;
4454+
4455+ (*
4456+ This is the core of the implementation. It doesn't emit bytecode directly.
4457+ Instead, it builds an equivalent C-style XTree_For node and then compiles that.
4458+ This is the "desugaring" process.
4459+ *)
4460+ function XTree_ForIn.Compile (Dest: TXprVar; Flags: TCompilerFlags): TXprVar;
4461+ var
4462+ // Nodes for the generated C-style for loop
4463+ highVarDecl, indexVarDecl: XTree_VarDecl;
4464+ forLoopCondition, forLoopIncrement: XTree_Node;
4465+ forLoop: XTree_For;
4466+ itemAssignment: XTree_Node;
4467+ newBody: XTree_ExprList;
4468+
4469+ // Names for compiler-generated internal variables
4470+ highVarName, indexVarName: string;
4471+
4472+ // Type information
4473+ collectionType: XType;
4474+ itemType, ptrType: XType;
4475+ refItemVar: TXprVar;
4476+ _ptrIdx: Int32;
4477+ begin
4478+ // --- 1. Type Checking ---
4479+ collectionType := Self.Collection.ResType();
4480+
4481+ // todo: once classes has properties, allow classes!
4482+ if not (collectionType is XType_Array) then // Also covers XType_String
4483+ ctx.RaiseExceptionFmt(' Cannot iterate over non-array type `%s` in a for-in loop.' ,
4484+ [collectionType.ToString()], Self.Collection.FDocPos);
4485+
4486+ itemType := (collectionType as XType_Array).ItemType;
4487+
4488+ highVarName := ' !h' ;
4489+ indexVarName := ' !i' ;
4490+
4491+ // --- 3. Build the AST for: `var !high := collection.High()` ---
4492+ highVarDecl := XTree_VarDecl.Create(
4493+ highVarName,
4494+ XTree_Invoke.Create(
4495+ XTree_Identifier.Create(' High' , ctx, Self.Collection.FDocPos),
4496+ [], ctx, Self.Collection.FDocPos
4497+ ),
4498+ nil , False, ctx, FDocPos
4499+ );
4500+ XTree_Invoke(highVarDecl.Expr).SelfExpr := Self.Collection; // Attach the collection to the invoke node
4501+
4502+
4503+ // Entry: `var index := 0`
4504+ indexVarDecl := XTree_VarDecl.Create(
4505+ indexVarName,
4506+ XTree_Int.Create(' 0' , ctx, FDocPos),
4507+ nil , False, ctx, FDocPos
4508+ );
4509+
4510+ // Condition: `index <= __high`
4511+ forLoopCondition := XTree_BinaryOp.Create(
4512+ op_LTE,
4513+ XTree_Identifier.Create(indexVarName, ctx, FDocPos),
4514+ XTree_Identifier.Create(highVarName, ctx, FDocPos),
4515+ ctx, FDocPos
4516+ );
4517+
4518+ // Increment: `index += 1`
4519+ forLoopIncrement := XTree_Assign.Create(op_Asgn,
4520+ XTree_Identifier.Create(indexVarName, ctx, FDocPos),
4521+ XTree_BinaryOp.Create(
4522+ op_add,
4523+ XTree_Identifier.Create(indexVarName, ctx, FDocPos),
4524+ XTree_Int.Create(' 1' , ctx, FDocPos),
4525+ ctx, Self.Collection.FDocPos
4526+ ),
4527+ ctx, FDocPos
4528+ );
4529+
4530+ // `var expr := collection[__index]`
4531+ if DeclareIdent = 1 then
4532+ begin
4533+ if Self.ItemVar is XTree_Identifier then
4534+ begin
4535+ itemAssignment := XTree_VarDecl.Create(
4536+ XTree_Identifier(Self.ItemVar).Name ,
4537+ XTree_Index.Create(
4538+ Self.Collection,
4539+ XTree_Identifier.Create(indexVarName, ctx, FDocPos),
4540+ ctx, FDocPos
4541+ ),
4542+ itemType,
4543+ // Explicitly provide the type
4544+ False, ctx, Self.ItemVar.FDocPos
4545+ );
4546+ end else if Self.ItemVar is XTree_Destructure then
4547+ begin
4548+ itemAssignment := XTree_DestructureDecl.Create(
4549+ XTree_Destructure(Self.ItemVar),
4550+ XTree_Index.Create(
4551+ Self.Collection,
4552+ XTree_Identifier.Create(indexVarName, ctx, FDocPos),
4553+ ctx, FDocPos
4554+ ),
4555+ ctx, Self.ItemVar.FDocPos
4556+ );
4557+ end else
4558+ ctx.RaiseException(' Illegal var declaration in for loop' , FDocPos);
4559+ end else
4560+ // `ref ident := collection[__index]`
4561+ if DeclareIdent = 2 then
4562+ begin
4563+ if not (Self.ItemVar is XTree_Identifier) then
4564+ ctx.RaiseException(' Reference variable in for-in-loop must be an identifier' , Self.ItemVar.FDocPos);
4565+
4566+ refItemVar := ctx.RegVar(XTree_Identifier(Self.ItemVar).Name , ctx.GetType(xtPointer), FDocPos, _ptrIdx);
4567+ itemAssignment := XTree_Assign.Create(op_Asgn,
4568+ XTree_VarStub.Create(refItemVar, ctx, FDocPos),
4569+ XTree_UnaryOp.Create(op_Addr,
4570+ XTree_Index.Create(Self.Collection, XTree_Identifier.Create(indexVarName, ctx, FDocPos), ctx, FDocPos),
4571+ ctx, FDocPos
4572+ ),
4573+ ctx, FDocPos
4574+ );
4575+
4576+ // all future uses is reference uses
4577+ ctx.Variables.Data[_ptrIdx].Reference := True;
4578+ ctx.Variables.Data[_ptrIdx].VarType := ItemType;
4579+ end else
4580+ begin
4581+ itemAssignment := XTree_Assign.Create(op_asgn,
4582+ Self.ItemVar,
4583+ XTree_Index.Create(
4584+ Self.Collection,
4585+ XTree_Identifier.Create(indexVarName, ctx, FDocPos),
4586+ ctx, FDocPos
4587+ ),
4588+ ctx, Self.ItemVar.FDocPos
4589+ );
4590+ end ;
4591+
4592+ // Create the new body, starting with our assignment, then adding the user's code.
4593+ newBody := XTree_ExprList.Create(ctx, FDocPos);
4594+
4595+ SetLength(newBody.List, Length(Self.Body.List)+1 );
4596+ if Length(Self.Body.List) > 0 then
4597+ Move(Self.Body.List[0 ], NewBody.List[1 ], Length(Self.Body.List)*SizeOf(XTree_Node));
4598+
4599+ newBody.List[0 ] := itemAssignment;
4600+
4601+ forLoop := XTree_For.Create(
4602+ indexVarDecl,
4603+ forLoopCondition,
4604+ forLoopIncrement,
4605+ newBody,
4606+ ctx, FDocPos
4607+ );
4608+
4609+ try
4610+ // Compile the pre-loop statement: var !high := ...
4611+ highVarDecl.Compile(NullResVar, Flags);
4612+
4613+ // Compile the entire generated for-loop structure
4614+ forLoop.Compile(NullResVar, Flags);
4615+ finally
4616+ highVarDecl.Free;
4617+ forLoop.Free;
4618+ end ;
4619+
4620+ Result := NullResVar;
4621+ end ;
4622+
4623+
43994624
44004625// ============================================================================
44014626// REPEAT-UNTIL loop
@@ -4593,8 +4818,10 @@ function XTree_UnaryOp.Compile(Dest: TXprVar; Flags: TCompilerFlags): TXprVar;
45934818 if not LeftVar.Reference then
45944819 ctx.Emit(GetInstr(OP2IC(OP), [Result, LeftVar]), FDocPos)
45954820 else
4821+ begin
45964822 Result := LeftVar;
4597-
4823+ Result.VarType := Self.ResType();
4824+ end ;
45984825 Result.Reference := False;
45994826 end ;
46004827
0 commit comments