Skip to content

Commit 566fc51

Browse files
Patrick Quistclaude
andcommitted
Accept a constant expression as an array bound
An array bound may be any constant expression, but OrdinalType only ever accepted a constant or a type name: const mlab = 4; mlog = 12; type TRanges = record iu: array[mlab + 1..mlog] of Integer; end; failed with 'SquareClose' expected found '+'. OrdinalType decided on a single token of lookahead: an identifier followed by anything but '(' or '..' was a type name, so it read the bound as the type `mlab`, returned, and left ArrayBounds looking for the ']' it found a '+' at. The upper bound already worked, which is what makes the gap easy to miss. `array[mlab..mlog + 1]` sends the first bound to ConstantExpression on the '..' lookahead, and OrdinalType's own trailing '..' branch parses the rest as an expression. Only the first bound of a subrange went down the type-name path. The lookahead now also routes the operators SimpleExpression and Term accept to ConstantExpression. Every one of those tokens is a parse error in this position today, so no input that parses now takes a different path: an identifier in an OrdinalType is followed by ']', ',', '..', 'of' or ';', never by an operator. Set types and variant record tag types go through the same procedure and gain the same forms. A single token is enough here and a full ahead-parse would be worse. Coming from the identifier, SimpleType's `AheadParse.NextToken; AheadParse.Simple- Expression` idiom would meet the ']' of the common `array[TIndex] of Byte` and hand it to Factor as a set constructor. Parenthesized bounds are deliberately left alone. `array[(mlab + 1)..mlog]` goes to EnumeratedType, and dcc32 reads it the same way - it reports "Identifier redeclared: 'mlab'" - so the parser already agrees with the compiler. Test/Snippets/arrayboundexpression.pas covers the first bound, both bounds, two dimensions, `*`, `-` and `shl`, and a set of a computed subrange; dcc32 compiles it clean. Without the parser change it fails with the original 'SquareClose' expected found '+', so it guards the actual bug. Suite: 43 tests, 42 passing, with the pre-existing Serialization.BinaryRoundTrip failure unchanged (line_seq holds a pointer value that does not survive a round trip). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent e89ce1f commit 566fc51

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

‎Source/SimpleParser/SimpleParser.pas‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3648,6 +3648,16 @@ procedure TmwSimplePasPar.OrdinalType;
36483648
begin
36493649
ConstantExpression;
36503650
end;
3651+
{ An operator after the identifier means the bound is a constant
3652+
expression, not a type name: array[mlab + 1..mlog]. Reading it as a
3653+
type name consumes the identifier alone and leaves the caller at the
3654+
operator, where it can only report an error. These are the operators
3655+
SimpleExpression and Term accept. }
3656+
ptAnd, ptDiv, ptMinus, ptMod, ptOr, ptPlus, ptShl, ptShr, ptSlash,
3657+
ptStar, ptXor:
3658+
begin
3659+
ConstantExpression;
3660+
end;
36513661
else
36523662
begin
36533663
TypeID;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
unit arrayboundexpression;
2+
3+
interface
4+
5+
const
6+
mlab = 4;
7+
mlog = 12;
8+
9+
type
10+
{ An array bound is a constant EXPRESSION, not just a constant or a type name.
11+
OrdinalType decided on one token of lookahead and read `mlab` as a type name,
12+
so the whole unit failed to parse at the `+`. }
13+
TRanges = record
14+
iu: array[mlab + 1..mlog] of Integer;
15+
du: array[mlab * 2..mlog - 1, 0..mlab shl 1] of Byte;
16+
end;
17+
18+
TSetOfExpression = set of mlab + 1..mlog;
19+
20+
implementation
21+
22+
end.

0 commit comments

Comments
 (0)