fix(arrays): enum-indexed multi-dimensional arrays (#128)
A multi-dimensional array whose dimensions are enum types — array[TEnum, TEnum] or array[TEnum, 0..1] — failed to parse with "Expected '..' but got ','". Only the single-dimension form array[TEnum] was handled; every dimension in the comma-separated multi-dim path was forced through the lo..hi range parser. Both the const-array dimension parser (ReadConstArrayDim) and the type-name parser (ParseTypeName) now recognise an enum-type dimension — a bare identifier not followed by '..' — and encode it as the range 0 .. @TEnum, reusing the existing single-dimension '@TEnum' convention. ResolveArrayBound resolves the '@TEnum' high-bound marker to the enum's last ordinal (member count - 1). This fixes const, type and var declarations in any dimension position and mix (enum+range, range+enum, 3-D enum+range+enum). Adds dual-backend e2e coverage: 2-D enum/enum, mixed dims in both orders, type+var form, and a 3-D mixed case. All three fixpoints green; 3499 tests pass.
This commit is contained in:
parent
517ed26f5d
commit
937146a3ac
|
|
@ -269,9 +269,23 @@ begin
|
|||
Highs := TStringList.Create();
|
||||
try
|
||||
repeat
|
||||
Lows.Add(Self.ReadConstBoundText());
|
||||
Expect(tkDotDot);
|
||||
Highs.Add(Self.ReadConstBoundText());
|
||||
{ An enum-type dimension is a bare identifier NOT followed by '..'
|
||||
(e.g. the TEnum dimensions in 'array[TEnum, 0..1]'). Encode it as
|
||||
0 .. @TEnum so the range-indexed type resolver folds the high bound
|
||||
to the enum's last ordinal — the same '@TEnum' convention used for
|
||||
the single-dimension 'array[TEnum]' form above. }
|
||||
if Check(tkIdent) and (PeekKind() <> tkDotDot) then
|
||||
begin
|
||||
Lows.Add('0');
|
||||
Highs.Add('@' + FCurrent.Value);
|
||||
Advance();
|
||||
end
|
||||
else
|
||||
begin
|
||||
Lows.Add(Self.ReadConstBoundText());
|
||||
Expect(tkDotDot);
|
||||
Highs.Add(Self.ReadConstBoundText());
|
||||
end;
|
||||
if not Check(tkComma) then
|
||||
Break;
|
||||
Advance(); { consume ',' — next dimension }
|
||||
|
|
@ -1031,6 +1045,18 @@ procedure TParser.ReadConstArrayDim(CD: TConstDecl);
|
|||
var
|
||||
LoText, HiText: string;
|
||||
begin
|
||||
{ An enum-type dimension is a bare identifier NOT followed by '..' (e.g. the
|
||||
TEnum in 'array[TEnum, 0..1]'). Encode it as the range 0 .. @TEnum so the
|
||||
semantic pass resolves the high bound to the enum's last ordinal — the same
|
||||
convention used for single-dimension 'array[@TEnum]'. }
|
||||
if Check(tkIdent) and (PeekKind() <> tkDotDot) then
|
||||
begin
|
||||
CD.ArrayDimLows.Add('0');
|
||||
CD.ArrayDimHighs.Add('@' + FCurrent.Value);
|
||||
Advance();
|
||||
CD.ArrayIsRangeIndexed := True;
|
||||
Exit;
|
||||
end;
|
||||
LoText := Self.ReadConstBoundText();
|
||||
Expect(tkDotDot);
|
||||
HiText := Self.ReadConstBoundText();
|
||||
|
|
|
|||
|
|
@ -3907,6 +3907,18 @@ var
|
|||
begin
|
||||
if IsPlainInt(ABoundText) then
|
||||
Exit(Integer(StrToInt(ABoundText)));
|
||||
{ Enum-type dimension marker '@TEnum' (see ReadConstArrayDim): the high bound
|
||||
of an enum-indexed dimension is the enum's last ordinal (member count - 1).
|
||||
The low bound is always 0, stored as plain '0'. }
|
||||
if (Length(ABoundText) > 0) and (ABoundText[0] = '@') then
|
||||
begin
|
||||
Sym := FTable.Lookup(Copy(ABoundText, 1, Length(ABoundText) - 1));
|
||||
if (Sym <> nil) and (Sym.Kind = skType) and (Sym.TypeDesc <> nil) and
|
||||
(Sym.TypeDesc.Kind = tyEnum) then
|
||||
Exit(TEnumTypeDesc(Sym.TypeDesc).Members.Count - 1);
|
||||
SemanticError(Format('Unknown enum type ''%s'' in array dimension',
|
||||
[Copy(ABoundText, 1, Length(ABoundText) - 1)]), 0, 0);
|
||||
end;
|
||||
Sym := FTable.Lookup(ABoundText);
|
||||
if (Sym <> nil) and (Sym.Kind = skConstant) then
|
||||
Exit(Integer(Sym.ConstValue));
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ type
|
|||
procedure TestRun_ArrayConst_ElementWidths;
|
||||
procedure TestRun_ArrayConst_NegativeInts;
|
||||
procedure TestRun_ArrayConst_EnumIndexedBool;
|
||||
{ Enum-indexed multi-dimensional arrays (GitHub #128) }
|
||||
procedure TestRun_EnumBidimArray_Const;
|
||||
procedure TestRun_EnumBidimArray_MixedDims;
|
||||
procedure TestRun_EnumBidimArray_TypeAndVar;
|
||||
procedure TestRun_EnumBidimArray_ThreeDims;
|
||||
|
||||
{ Static array return by value (GitHub #112) }
|
||||
procedure TestRun_StaticArrayReturn_12Bytes;
|
||||
|
|
@ -915,6 +920,90 @@ begin
|
|||
'open' + Chr(10) + 'closed' + Chr(10) + 'open' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
procedure TE2EGapTests.TestRun_EnumBidimArray_Const;
|
||||
{ GitHub #128: a 2-D array const indexed by an enum in both dimensions —
|
||||
array[TEnum, TEnum] — previously failed to parse ("Expected '..' but got
|
||||
','"). }
|
||||
const
|
||||
Src =
|
||||
'''
|
||||
program P;
|
||||
type TEnum = (one, two);
|
||||
const d: array[TEnum, TEnum] of byte = ((1, 2), (3, 4));
|
||||
begin
|
||||
WriteLn(d[one, one]);
|
||||
WriteLn(d[one, two]);
|
||||
WriteLn(d[two, one]);
|
||||
WriteLn(d[two, two])
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
AssertRunsOnAll(Src,
|
||||
'1' + Chr(10) + '2' + Chr(10) + '3' + Chr(10) + '4' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
procedure TE2EGapTests.TestRun_EnumBidimArray_MixedDims;
|
||||
{ Mixed dimensions: an enum dimension and an integer-range dimension together,
|
||||
in both orders. }
|
||||
const
|
||||
Src =
|
||||
'''
|
||||
program P;
|
||||
type TE = (a, b);
|
||||
const
|
||||
e: array[TE, 0..1] of byte = ((5, 6), (7, 8));
|
||||
f: array[0..1, TE] of byte = ((10, 20), (30, 40));
|
||||
begin
|
||||
WriteLn(e[a, 0]);
|
||||
WriteLn(e[b, 1]);
|
||||
WriteLn(f[0, a]);
|
||||
WriteLn(f[1, b])
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
AssertRunsOnAll(Src,
|
||||
'5' + Chr(10) + '8' + Chr(10) + '10' + Chr(10) + '40' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
procedure TE2EGapTests.TestRun_EnumBidimArray_TypeAndVar;
|
||||
{ The same enum-indexed multi-dim form in a type declaration and a var. }
|
||||
const
|
||||
Src =
|
||||
'''
|
||||
program P;
|
||||
type
|
||||
TE = (a, b, c);
|
||||
TGrid = array[TE, TE] of byte;
|
||||
var g: TGrid;
|
||||
begin
|
||||
g[a, c] := 5;
|
||||
g[c, a] := 9;
|
||||
WriteLn(g[a, c]);
|
||||
WriteLn(g[c, a])
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
AssertRunsOnAll(Src, '5' + Chr(10) + '9' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
procedure TE2EGapTests.TestRun_EnumBidimArray_ThreeDims;
|
||||
{ Three dimensions, enum + range + enum. }
|
||||
const
|
||||
Src =
|
||||
'''
|
||||
program P;
|
||||
type TE = (a, b);
|
||||
const m: array[TE, 0..1, TE] of byte =
|
||||
(((1,2),(3,4)), ((5,6),(7,8)));
|
||||
begin
|
||||
WriteLn(m[a, 0, a]);
|
||||
WriteLn(m[b, 1, b])
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
AssertRunsOnAll(Src, '1' + Chr(10) + '8' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
procedure TE2EGapTests.TestRun_IntfSret_SixArgs_DirectClassCall;
|
||||
{ Class instance whose method returns an interface and takes 6 Integer args.
|
||||
On native this is the class-sret interface call path (>4 user arg slots);
|
||||
|
|
|
|||
Loading…
Reference in a new issue