2026-04-22 21:00:46 +03:00
|
|
|
{
|
|
|
|
|
Blaise - An Object Pascal Compiler
|
|
|
|
|
Copyright (c) 2026 Graeme Geldenhuys
|
2026-05-03 21:45:29 +03:00
|
|
|
SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
|
|
|
|
|
Licensed under the Apache License v2.0 with Runtime Library Exception.
|
2026-04-22 21:00:46 +03:00
|
|
|
See LICENSE file in the project root for full license terms.
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 18:58:36 +03:00
|
|
|
program Phase3Milestone;
|
|
|
|
|
|
|
|
|
|
{ Phase 3 milestone: exercises TList<Integer> and TDictionary<string,Integer>.
|
|
|
|
|
Must produce correct output and zero valgrind leaks. }
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
TIntList = class
|
|
|
|
|
FData: ^Integer;
|
|
|
|
|
FCount: Integer;
|
|
|
|
|
FCapacity: Integer;
|
|
|
|
|
procedure Grow;
|
|
|
|
|
procedure Add(Value: Integer);
|
|
|
|
|
function Get(AIndex: Integer): Integer;
|
|
|
|
|
procedure Delete(AIndex: Integer);
|
2026-04-23 12:51:56 +03:00
|
|
|
procedure Destroy;
|
2026-04-22 18:58:36 +03:00
|
|
|
property Count: Integer read FCount;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TStrIntDict = class
|
|
|
|
|
FKeys: ^string;
|
|
|
|
|
FValues: ^Integer;
|
|
|
|
|
FCount: Integer;
|
|
|
|
|
FCapacity: Integer;
|
|
|
|
|
procedure Grow;
|
|
|
|
|
function FindKey(Key: string): Integer;
|
|
|
|
|
procedure Add(Key: string; Value: Integer);
|
|
|
|
|
function TryGetValue(Key: string; var Value: Integer): Boolean;
|
|
|
|
|
function ContainsKey(Key: string): Boolean;
|
|
|
|
|
procedure Remove(Key: string);
|
2026-04-23 12:51:56 +03:00
|
|
|
procedure Destroy;
|
2026-04-22 18:58:36 +03:00
|
|
|
property Count: Integer read FCount;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TIntList.Grow;
|
|
|
|
|
var
|
|
|
|
|
NewCap: Integer;
|
|
|
|
|
begin
|
|
|
|
|
if Self.FCapacity = 0 then
|
|
|
|
|
NewCap := 4
|
|
|
|
|
else
|
|
|
|
|
NewCap := Self.FCapacity * 2;
|
|
|
|
|
Self.FData := ReallocMem(Self.FData, NewCap * SizeOf(Integer));
|
|
|
|
|
Self.FCapacity := NewCap
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TIntList.Add(Value: Integer);
|
|
|
|
|
var
|
|
|
|
|
Dest: ^Integer;
|
|
|
|
|
begin
|
|
|
|
|
if Self.FCount = Self.FCapacity then
|
2026-07-04 21:44:54 +03:00
|
|
|
Self.Grow;
|
2026-04-22 18:58:36 +03:00
|
|
|
Dest := Self.FData + Self.FCount * SizeOf(Integer);
|
|
|
|
|
Dest^ := Value;
|
|
|
|
|
Self.FCount := Self.FCount + 1
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TIntList.Get(AIndex: Integer): Integer;
|
|
|
|
|
var
|
|
|
|
|
Src: ^Integer;
|
|
|
|
|
begin
|
|
|
|
|
Src := Self.FData + AIndex * SizeOf(Integer);
|
|
|
|
|
Result := Src^
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TIntList.Delete(AIndex: Integer);
|
|
|
|
|
var
|
|
|
|
|
Src: ^Integer;
|
|
|
|
|
Dst: ^Integer;
|
|
|
|
|
I: Integer;
|
|
|
|
|
begin
|
|
|
|
|
I := AIndex;
|
|
|
|
|
while I < Self.FCount - 1 do
|
|
|
|
|
begin
|
|
|
|
|
Dst := Self.FData + I * SizeOf(Integer);
|
|
|
|
|
Src := Self.FData + (I + 1) * SizeOf(Integer);
|
|
|
|
|
Dst^ := Src^;
|
|
|
|
|
I := I + 1
|
|
|
|
|
end;
|
|
|
|
|
Self.FCount := Self.FCount - 1
|
|
|
|
|
end;
|
|
|
|
|
|
2026-04-23 12:51:56 +03:00
|
|
|
procedure TIntList.Destroy;
|
2026-04-22 18:58:36 +03:00
|
|
|
begin
|
|
|
|
|
FreeMem(Self.FData);
|
2026-04-23 12:51:56 +03:00
|
|
|
Self.FData := nil;
|
|
|
|
|
Self.FCount := 0;
|
|
|
|
|
Self.FCapacity := 0
|
2026-04-22 18:58:36 +03:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TStrIntDict.Grow;
|
|
|
|
|
var
|
|
|
|
|
NewCap: Integer;
|
2026-04-23 14:32:59 +03:00
|
|
|
OldCap: Integer;
|
2026-04-22 18:58:36 +03:00
|
|
|
begin
|
2026-04-23 14:32:59 +03:00
|
|
|
OldCap := Self.FCapacity;
|
|
|
|
|
if OldCap = 0 then
|
2026-04-22 18:58:36 +03:00
|
|
|
NewCap := 8
|
|
|
|
|
else
|
2026-04-23 14:32:59 +03:00
|
|
|
NewCap := OldCap * 2;
|
2026-04-22 18:58:36 +03:00
|
|
|
Self.FKeys := ReallocMem(Self.FKeys, NewCap * SizeOf(string));
|
2026-04-23 14:32:59 +03:00
|
|
|
{ Zero-init new string slots so ARC Release of "old" value is safe }
|
|
|
|
|
ZeroMem(Self.FKeys + OldCap * SizeOf(string),
|
|
|
|
|
(NewCap - OldCap) * SizeOf(string));
|
2026-04-22 18:58:36 +03:00
|
|
|
Self.FValues := ReallocMem(Self.FValues, NewCap * SizeOf(Integer));
|
|
|
|
|
Self.FCapacity := NewCap
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TStrIntDict.FindKey(Key: string): Integer;
|
|
|
|
|
var
|
|
|
|
|
I: Integer;
|
|
|
|
|
Ptr: ^string;
|
|
|
|
|
begin
|
|
|
|
|
Result := -1;
|
|
|
|
|
I := 0;
|
|
|
|
|
while I < Self.FCount do
|
|
|
|
|
begin
|
|
|
|
|
Ptr := Self.FKeys + I * SizeOf(string);
|
|
|
|
|
if Ptr^ = Key then
|
|
|
|
|
begin
|
|
|
|
|
Result := I;
|
2026-04-23 01:18:47 +03:00
|
|
|
break
|
|
|
|
|
end;
|
|
|
|
|
I := I + 1
|
2026-04-22 18:58:36 +03:00
|
|
|
end
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TStrIntDict.Add(Key: string; Value: Integer);
|
|
|
|
|
var
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
KPtr: ^string;
|
|
|
|
|
VPtr: ^Integer;
|
|
|
|
|
begin
|
|
|
|
|
Idx := Self.FindKey(Key);
|
|
|
|
|
if Idx >= 0 then
|
|
|
|
|
begin
|
|
|
|
|
VPtr := Self.FValues + Idx * SizeOf(Integer);
|
|
|
|
|
VPtr^ := Value
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
if Self.FCount = Self.FCapacity then
|
2026-07-04 21:44:54 +03:00
|
|
|
Self.Grow;
|
2026-04-22 18:58:36 +03:00
|
|
|
KPtr := Self.FKeys + Self.FCount * SizeOf(string);
|
|
|
|
|
VPtr := Self.FValues + Self.FCount * SizeOf(Integer);
|
|
|
|
|
KPtr^ := Key;
|
|
|
|
|
VPtr^ := Value;
|
|
|
|
|
Self.FCount := Self.FCount + 1
|
|
|
|
|
end
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TStrIntDict.TryGetValue(Key: string; var Value: Integer): Boolean;
|
|
|
|
|
var
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
VPtr: ^Integer;
|
|
|
|
|
begin
|
|
|
|
|
Idx := Self.FindKey(Key);
|
|
|
|
|
if Idx >= 0 then
|
|
|
|
|
begin
|
|
|
|
|
VPtr := Self.FValues + Idx * SizeOf(Integer);
|
|
|
|
|
Value := VPtr^;
|
|
|
|
|
Result := True
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Result := False
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TStrIntDict.ContainsKey(Key: string): Boolean;
|
|
|
|
|
begin
|
|
|
|
|
Result := Self.FindKey(Key) >= 0
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TStrIntDict.Remove(Key: string);
|
|
|
|
|
var
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
I: Integer;
|
|
|
|
|
KDst: ^string;
|
|
|
|
|
KSrc: ^string;
|
|
|
|
|
VDst: ^Integer;
|
|
|
|
|
VSrc: ^Integer;
|
|
|
|
|
begin
|
|
|
|
|
Idx := Self.FindKey(Key);
|
|
|
|
|
if Idx >= 0 then
|
|
|
|
|
begin
|
|
|
|
|
I := Idx;
|
|
|
|
|
while I < Self.FCount - 1 do
|
|
|
|
|
begin
|
|
|
|
|
KDst := Self.FKeys + I * SizeOf(string);
|
|
|
|
|
KSrc := Self.FKeys + (I + 1) * SizeOf(string);
|
|
|
|
|
VDst := Self.FValues + I * SizeOf(Integer);
|
|
|
|
|
VSrc := Self.FValues + (I + 1) * SizeOf(Integer);
|
|
|
|
|
KDst^ := KSrc^;
|
|
|
|
|
VDst^ := VSrc^;
|
|
|
|
|
I := I + 1
|
|
|
|
|
end;
|
2026-04-23 14:32:59 +03:00
|
|
|
{ Release the duplicate last string slot left behind by the shift }
|
|
|
|
|
KDst := Self.FKeys + (Self.FCount - 1) * SizeOf(string);
|
|
|
|
|
KDst^ := nil;
|
2026-04-22 18:58:36 +03:00
|
|
|
Self.FCount := Self.FCount - 1
|
|
|
|
|
end
|
|
|
|
|
end;
|
|
|
|
|
|
2026-04-23 12:51:56 +03:00
|
|
|
procedure TStrIntDict.Destroy;
|
2026-04-23 14:32:59 +03:00
|
|
|
var
|
|
|
|
|
I: Integer;
|
|
|
|
|
Ptr: ^string;
|
2026-04-22 18:58:36 +03:00
|
|
|
begin
|
2026-04-23 14:32:59 +03:00
|
|
|
{ Release all stored strings before freeing the backing store }
|
|
|
|
|
I := 0;
|
|
|
|
|
while I < Self.FCount do
|
|
|
|
|
begin
|
|
|
|
|
Ptr := Self.FKeys + I * SizeOf(string);
|
|
|
|
|
Ptr^ := nil;
|
|
|
|
|
I := I + 1
|
|
|
|
|
end;
|
2026-04-22 18:58:36 +03:00
|
|
|
FreeMem(Self.FKeys);
|
|
|
|
|
FreeMem(Self.FValues);
|
2026-04-23 12:51:56 +03:00
|
|
|
Self.FKeys := nil;
|
|
|
|
|
Self.FValues := nil;
|
|
|
|
|
Self.FCount := 0;
|
|
|
|
|
Self.FCapacity := 0
|
2026-04-22 18:58:36 +03:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
var
|
|
|
|
|
List: TIntList;
|
|
|
|
|
Dict: TStrIntDict;
|
|
|
|
|
V: Integer;
|
|
|
|
|
Found: Boolean;
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
{ --- TList<Integer> --- }
|
|
|
|
|
List := TIntList.Create;
|
|
|
|
|
List.Add(10);
|
|
|
|
|
List.Add(20);
|
|
|
|
|
List.Add(30);
|
|
|
|
|
List.Add(40);
|
|
|
|
|
List.Add(50);
|
|
|
|
|
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('list.count=', List.Count); { 5 }
|
|
|
|
|
WriteLn('list[0]=', List.Get(0)); { 10 }
|
|
|
|
|
WriteLn('list[4]=', List.Get(4)); { 50 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
|
|
|
|
List.Delete(1);
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('count_after_delete=', List.Count); { 4 }
|
|
|
|
|
WriteLn('list[1]_after_delete=', List.Get(1)); { 30 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
2026-07-04 21:44:54 +03:00
|
|
|
List.Free;
|
2026-04-22 18:58:36 +03:00
|
|
|
|
|
|
|
|
{ --- TDictionary<string,Integer> --- }
|
|
|
|
|
Dict := TStrIntDict.Create;
|
|
|
|
|
Dict.Add('alpha', 1);
|
|
|
|
|
Dict.Add('beta', 2);
|
|
|
|
|
Dict.Add('gamma', 3);
|
|
|
|
|
Dict.Add('delta', 4);
|
|
|
|
|
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('dict.count=', Dict.Count); { 4 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
|
|
|
|
Found := Dict.TryGetValue('beta', V);
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('beta=', V); { 2 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
|
|
|
|
Found := Dict.ContainsKey('gamma');
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('has_gamma=', Found); { 1 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
|
|
|
|
Dict.Add('beta', 99);
|
|
|
|
|
Found := Dict.TryGetValue('beta', V);
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('beta_after_update=', V); { 99 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
|
|
|
|
Dict.Remove('alpha');
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('count_after_remove=', Dict.Count); { 3 }
|
2026-04-22 18:58:36 +03:00
|
|
|
Found := Dict.ContainsKey('alpha');
|
2026-04-23 01:19:55 +03:00
|
|
|
WriteLn('has_alpha_after_remove=', Found); { 0 }
|
2026-04-22 18:58:36 +03:00
|
|
|
|
2026-07-04 21:44:54 +03:00
|
|
|
Dict.Free
|
2026-04-22 18:58:36 +03:00
|
|
|
end.
|