Skip to content

Commit 2ce511e

Browse files
committed
Fix parsing ambiguity around adjacent > and = tokens
1 parent 807251a commit 2ce511e

File tree

4 files changed

+59
-5
lines changed

4 files changed

+59
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Parsing errors where adjacent `>` and `=` tokens were wrongly interpreted as the `>=` operator.
13+
1014
## [1.13.0] - 2025-02-05
1115

1216
### Added

delphi-frontend/src/main/antlr3/au/com/integradev/delphi/antlr/Delphi.g

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ tokens {
7171
TkArgument;
7272
TkAnonymousMethod;
7373
TkAnonymousMethodHeading;
74+
TkLessThanEqual;
75+
TkGreaterThanEqual;
7476
}
7577

7678
@header
@@ -328,13 +330,19 @@ import org.apache.commons.lang3.StringUtils;
328330
return t;
329331
}
330332

331-
private Token combineLastNTokens(int count) {
333+
private Token combineLastNTokens(int type, int count) {
332334
CommonToken firstToken = (CommonToken) input.LT(-count);
333335
CommonToken lastToken = (CommonToken) input.LT(-1);
336+
lastToken.setType(type);
334337
lastToken.setStartIndex(firstToken.getStartIndex());
335338
return lastToken;
336339
}
337340

341+
private BinaryExpressionNodeImpl createBinaryExpression(Object operator) {
342+
Token token = adaptor.getToken(operator);
343+
return new BinaryExpressionNodeImpl(token);
344+
}
345+
338346
@Override
339347
public void reportError(RecognitionException e) {
340348
String hdr = this.getErrorHeader(e);
@@ -965,12 +973,21 @@ unaryOperator : NOT<UnaryExpressionNodeImpl>
965973
relationalOperator : '='<BinaryExpressionNodeImpl>
966974
| '>'<BinaryExpressionNodeImpl>
967975
| '<'<BinaryExpressionNodeImpl>
968-
| '<='<BinaryExpressionNodeImpl>
969-
| '>='<BinaryExpressionNodeImpl>
976+
| op=lessThanEqualOperator -> {createBinaryExpression(op.getTree())}
977+
| op=greaterThanEqualOperator -> {createBinaryExpression(op.getTree())}
970978
| '<>'<BinaryExpressionNodeImpl>
971979
| IN<BinaryExpressionNodeImpl>
972980
| IS<BinaryExpressionNodeImpl>
973981
;
982+
// We're only doing this for symmetry with greaterThanEqualOperator. (see comment below)
983+
lessThanEqualOperator : '<' '=' -> ^({combineLastNTokens(TkLessThanEqual, 2)})
984+
;
985+
// We construct the "greater than equal" tokens while parsing binary expressions to preserve the
986+
// individual '>' and '=' tokens in other cases like `const Foo: TArray<Byte>=[1, 2, 3];`, which
987+
// we otherwise couldn't parse since the `>=` token would consume the closing angle bracket of the
988+
// generic type arguments and the const assignment operator.
989+
greaterThanEqualOperator : '>' '=' -> ^({combineLastNTokens(TkGreaterThanEqual, 2)})
990+
;
974991
constExpression : expression
975992
| recordExpression
976993
| arrayExpression
@@ -1372,8 +1389,6 @@ COLON : ':' ;
13721389
EQUAL : '=' ;
13731390
NOT_EQUAL : '<>' ;
13741391
LESS_THAN : '<' ;
1375-
LESS_THAN_EQUAL : '<=' ;
1376-
GREATER_THAN_EQUAL : '>=' ;
13771392
GREATER_THAN : '>' ;
13781393
SQUARE_BRACKET_LEFT : '[' ;
13791394
SQUARE_BRACKET_RIGHT : ']' ;

delphi-frontend/src/test/java/au/com/integradev/delphi/antlr/GrammarTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,4 +353,9 @@ void testConditionalAsm() {
353353
void testSemicolonSeparatedGenericArguments() {
354354
assertParsed("SemicolonSeparatedGenericArguments.pas");
355355
}
356+
357+
@Test
358+
void testGreaterThanEqualAmbiguity() {
359+
assertParsed("GreaterThanEqualAmbiguity.pas");
360+
}
356361
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
unit GreaterThanEqualAmbiguity;
2+
3+
interface
4+
5+
type
6+
TFoo = class(TObject)
7+
procedure Bar(Baz: IBaz<string>=nil);
8+
end;
9+
10+
implementation
11+
12+
procedure TFoo.Bar(Baz: IBaz<string>=nil);
13+
begin
14+
// do nothing
15+
end;
16+
17+
function Flarp: TArray<Byte>;
18+
const
19+
Bytes: TArray<Byte>=[1, 2, 3];
20+
begin
21+
Result := Bytes;
22+
end;
23+
24+
function FlimFlam: TArray<Byte>;
25+
begin
26+
const Bytes: TArray<Byte>=[1, 2, 3];
27+
Result := Bytes;
28+
end;
29+
30+
end.

0 commit comments

Comments
 (0)