Skip to content

Commit c6f9ec1

Browse files
committed
fix parser for 'ULINE AT /pos(len)' (#80)
1 parent 7d55f77 commit c6f9ec1

File tree

3 files changed

+171
-3
lines changed

3 files changed

+171
-3
lines changed

com.sap.adt.abapcleaner/src/com/sap/adt/abapcleaner/parser/Token.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,9 +1133,11 @@ final ColorType getMainColorType() {
11331133
return isStringLiteral() ? ColorType.STRING_LITERAL : ColorType.NUMBER;
11341134

11351135
case ASSIGNMENT_OP:
1136-
case OTHER_OP:
11371136
return ColorType.USUAL_OPERATOR;
11381137

1138+
case OTHER_OP:
1139+
return textEquals(")") && isAttached() ? ColorType.TOKEN_OPERATOR : ColorType.USUAL_OPERATOR;
1140+
11391141
case COMPARISON_OP:
11401142
if (!StringUtil.isNullOrEmpty(text) && Character.isLetter(text.charAt(0))) // GE, GT, EQ, NE etc.
11411143
return ColorType.KEYWORD;
@@ -1179,10 +1181,12 @@ final TextBit[] toTextBits(int startIndex) {
11791181

11801182
// in most cases, the whole Token is of one ColorType
11811183
ColorType colType = getMainColorType();
1182-
if (type != TokenType.KEYWORD && type != TokenType.IDENTIFIER && type != TokenType.LITERAL)
1184+
if (type != TokenType.KEYWORD && type != TokenType.IDENTIFIER && type != TokenType.LITERAL && type != TokenType.OTHER_OP)
11831185
isSimpleCase = true;
11841186
if (type == TokenType.IDENTIFIER && text.charAt(0) == ABAP.FIELD_SYMBOL_START_SIGN)
11851187
isSimpleCase = true;
1188+
if (type == TokenType.OTHER_OP && !textEndsWithAny("(", ")")) // "ULINE AT /(20)."
1189+
isSimpleCase = true;
11861190

11871191
// literals may be linked to a text symbol ID, having the form 'literal text'(idf), where 'idf' is always 3 characters long
11881192
if (type == TokenType.LITERAL) {
@@ -1195,7 +1199,7 @@ final TextBit[] toTextBits(int startIndex) {
11951199
TextBit.create(startIndex + literalLength, 1, ColorType.TOKEN_OPERATOR),
11961200
TextBit.create(startIndex + literalLength + 1, 3, ColorType.NUMBER),
11971201
TextBit.create(startIndex + literalLength + 4, 1, ColorType.TOKEN_OPERATOR)};
1198-
} else {
1202+
} else if (!textEndsWith(")")) { // "ULINE AT 10(20)."
11991203
isSimpleCase = true;
12001204
}
12011205
}
@@ -1209,9 +1213,16 @@ final TextBit[] toTextBits(int startIndex) {
12091213
for (int i = 0; i < text.length(); ++i) {
12101214
char c = text.charAt(i);
12111215
boolean isIdentifier = (type == TokenType.KEYWORD) ? ABAP.isCharAllowedForAnyKeyword(c) : ABAP.isCharAllowedForVariableNames(c);
1216+
// in some cases, initial '/' does NOT start an identifier with a namespace, e.g. "ULINE AT /.", "ULINE AT /10(20)." or "ULINE at /pos(20)."
1217+
if (i == 0 && c == '/' && (text.length() <= 1 || text.indexOf('/', 1) < 0)) {
1218+
isIdentifier = false;
1219+
}
12121220
if (i > 0 && isIdentifier != lastWasIdentifier) {
12131221
// if the text bit is not part of the keyword or the identifier, it is considered a token operator (e.g. "->", "=>", "(" etc.)
12141222
ColorType bitType = lastWasIdentifier ? colType : ColorType.TOKEN_OPERATOR;
1223+
// in some cases, a Token of type OTHER_OP contains a number, e.g. "ULINE AT /10(20)." and "ULINE AT 10(**)."
1224+
if (ABAP.isInteger(text.substring(writtenPos, i)))
1225+
bitType = ColorType.NUMBER;
12151226
result.add(TextBit.create(startIndex + writtenPos, i - writtenPos, bitType));
12161227
writtenPos = i;
12171228
}
@@ -1220,6 +1231,9 @@ final TextBit[] toTextBits(int startIndex) {
12201231
// process the last bit
12211232
if (writtenPos < text.length()) {
12221233
ColorType bitType = lastWasIdentifier ? colType : ColorType.TOKEN_OPERATOR;
1234+
// in some cases, a Token of type IDENTIFIER contains a number, e.g. "ULINE AT /10."
1235+
if (ABAP.isInteger(text.substring(writtenPos)))
1236+
bitType = ColorType.NUMBER;
12231237
result.add(TextBit.create(startIndex + writtenPos, text.length() - writtenPos, bitType));
12241238
}
12251239

com.sap.adt.abapcleaner/src/com/sap/adt/abapcleaner/parser/TokenTypeRefinerRnd.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ private void refine(Command command, Token token, com.sap.rnd.rndrt.Token rndTok
201201
// keywords may contain literals in the special case of Text Elements such as TEXT-001
202202
throwException = !token.textStartsWith("TEXT-");
203203
break;
204+
case OTHER_OP:
205+
// other operators may contain literals in the special case of "ULINE AT /10(20).", "ULINE AT 10(20)." etc.
206+
throwException = !token.textStartsWith("/") && !token.textEndsWith("(");
207+
break;
204208
default:
205209
throwException = true;
206210
break;

test/com.sap.adt.abapcleaner.test/src/com/sap/adt/abapcleaner/parser/CodeTest.java

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,4 +931,154 @@ void testPositionAndLengthWithLeadingZeros() {
931931

932932
testParseCode();
933933
}
934+
935+
@Test
936+
void testUlineAt() {
937+
// ensure that the parser accepts all the following ways of using ULINE [AT] (as does ABAP syntax)
938+
939+
buildSrc(" DATA pos TYPE i.");
940+
buildSrc(" DATA len TYPE i.");
941+
buildSrc("");
942+
buildSrc(" ULINE.");
943+
buildSrc("");
944+
buildSrc(" ULINE AT /.");
945+
buildSrc(" ULINE AT /(20).");
946+
buildSrc(" ULINE AT /(len).");
947+
buildSrc(" ULINE AT /(*).");
948+
buildSrc(" ULINE AT /(**).");
949+
buildSrc("");
950+
buildSrc(" ULINE AT /10.");
951+
buildSrc(" ULINE AT /10(20).");
952+
buildSrc(" ULINE AT /10(len).");
953+
buildSrc(" ULINE AT /10(*).");
954+
buildSrc(" ULINE AT /10(**).");
955+
buildSrc("");
956+
buildSrc(" ULINE AT /pos.");
957+
buildSrc(" ULINE AT /pos(20).");
958+
buildSrc(" ULINE AT /pos(len).");
959+
buildSrc(" ULINE AT /pos(*).");
960+
buildSrc(" ULINE AT /pos(**).");
961+
buildSrc("");
962+
buildSrc(" ULINE AT (20).");
963+
buildSrc(" ULINE AT (len).");
964+
buildSrc(" ULINE AT (*).");
965+
buildSrc(" ULINE AT (**).");
966+
buildSrc("");
967+
buildSrc(" ULINE AT 10.");
968+
buildSrc(" ULINE AT 10(20).");
969+
buildSrc(" ULINE AT 10(len).");
970+
buildSrc(" ULINE AT 10(*).");
971+
buildSrc(" ULINE AT 10(**).");
972+
buildSrc("");
973+
buildSrc(" ULINE AT pos.");
974+
buildSrc(" ULINE AT pos(20).");
975+
buildSrc(" ULINE AT pos(len).");
976+
buildSrc(" ULINE AT pos(*).");
977+
buildSrc(" ULINE AT pos(**).");
978+
buildSrc("");
979+
buildSrc(" ULINE /.");
980+
buildSrc(" ULINE /(20).");
981+
buildSrc(" ULINE /(len)."); // warning about missing 'AT', but no syntax error
982+
buildSrc(" ULINE /(*).");
983+
buildSrc(" ULINE /(**).");
984+
buildSrc("");
985+
buildSrc(" ULINE /10.");
986+
buildSrc(" ULINE /10(20).");
987+
buildSrc(" ULINE /10(len)."); // warning about missing 'AT', but no syntax error
988+
buildSrc(" ULINE /10(*).");
989+
buildSrc(" ULINE /10(**).");
990+
buildSrc("");
991+
buildSrc(" ULINE /pos."); // warning about missing 'AT', but no syntax error
992+
buildSrc(" ULINE /pos(20)."); // warning about missing 'AT', but no syntax error
993+
buildSrc(" ULINE /pos(len)."); // warning about missing 'AT', but no syntax error
994+
buildSrc(" ULINE /pos(*)."); // warning about missing 'AT', but no syntax error
995+
buildSrc(" ULINE /pos(**)."); // warning about missing 'AT', but no syntax error
996+
buildSrc("");
997+
buildSrc(" ULINE (20).");
998+
buildSrc(" ULINE (*).");
999+
buildSrc(" ULINE (**).");
1000+
buildSrc("");
1001+
buildSrc(" ULINE 10.");
1002+
buildSrc(" ULINE 10(20).");
1003+
buildSrc(" ULINE 10(*).");
1004+
buildSrc(" ULINE 10(**).");
1005+
1006+
putAnyMethodAroundSrc();
1007+
1008+
testParseCode();
1009+
}
1010+
1011+
@Test
1012+
void testWriteAt() {
1013+
// ensure that the parser accepts all the following ways of using WRITE [AT] (as does ABAP syntax)
1014+
1015+
buildSrc(" DATA pos TYPE i.");
1016+
buildSrc(" DATA len TYPE i.");
1017+
buildSrc("");
1018+
buildSrc(" WRITE AT / 'a'.");
1019+
buildSrc(" WRITE AT /(20) 'a'.");
1020+
buildSrc(" WRITE AT /(len) 'a'.");
1021+
buildSrc(" WRITE AT /(*) 'a'.");
1022+
buildSrc(" WRITE AT /(**) 'a'.");
1023+
buildSrc("");
1024+
buildSrc(" WRITE AT /10 'a'.");
1025+
buildSrc(" WRITE AT /10(20) 'a'.");
1026+
buildSrc(" WRITE AT /10(len) 'a'.");
1027+
buildSrc(" WRITE AT /10(*) 'a'.");
1028+
buildSrc(" WRITE AT /10(**) 'a'.");
1029+
buildSrc("");
1030+
buildSrc(" WRITE AT /pos 'a'.");
1031+
buildSrc(" WRITE AT /pos(20) 'a'.");
1032+
buildSrc(" WRITE AT /pos(len) 'a'.");
1033+
buildSrc(" WRITE AT /pos(*) 'a'.");
1034+
buildSrc(" WRITE AT /pos(**) 'a'.");
1035+
buildSrc("");
1036+
buildSrc(" WRITE AT (20) 'a'.");
1037+
buildSrc(" WRITE AT (len) 'a'.");
1038+
buildSrc(" WRITE AT (*) 'a'.");
1039+
buildSrc(" WRITE AT (**) 'a'.");
1040+
buildSrc("");
1041+
buildSrc(" WRITE AT 10 'a'.");
1042+
buildSrc(" WRITE AT 10(20) 'a'.");
1043+
buildSrc(" WRITE AT 10(len) 'a'.");
1044+
buildSrc(" WRITE AT 10(*) 'a'.");
1045+
buildSrc(" WRITE AT 10(**) 'a'.");
1046+
buildSrc("");
1047+
buildSrc(" WRITE AT pos 'a'.");
1048+
buildSrc(" WRITE AT pos(20) 'a'.");
1049+
buildSrc(" WRITE AT pos(len) 'a'.");
1050+
buildSrc(" WRITE AT pos(*) 'a'.");
1051+
buildSrc(" WRITE AT pos(**) 'a'.");
1052+
buildSrc("");
1053+
buildSrc(" WRITE / 'a'.");
1054+
buildSrc(" WRITE /(20) 'a'.");
1055+
buildSrc(" WRITE /(len) 'a'."); // warning about missing 'AT', but no syntax error
1056+
buildSrc(" WRITE /(*) 'a'.");
1057+
buildSrc(" WRITE /(**) 'a'.");
1058+
buildSrc("");
1059+
buildSrc(" WRITE /10 'a'.");
1060+
buildSrc(" WRITE /10(20) 'a'.");
1061+
buildSrc(" WRITE /10(len) 'a'."); // warning about missing 'AT', but no syntax error
1062+
buildSrc(" WRITE /10(*) 'a'.");
1063+
buildSrc(" WRITE /10(**) 'a'.");
1064+
buildSrc("");
1065+
buildSrc(" WRITE /pos 'a'."); // warning about missing 'AT', but no syntax error
1066+
buildSrc(" WRITE /pos(20) 'a'."); // warning about missing 'AT', but no syntax error
1067+
buildSrc(" WRITE /pos(len) 'a'."); // warning about missing 'AT', but no syntax error
1068+
buildSrc(" WRITE /pos(*) 'a'."); // warning about missing 'AT', but no syntax error
1069+
buildSrc(" WRITE /pos(**) 'a'."); // warning about missing 'AT', but no syntax error
1070+
buildSrc("");
1071+
buildSrc(" WRITE (20) 'a'.");
1072+
buildSrc(" WRITE (*) 'a'.");
1073+
buildSrc(" WRITE (**) 'a'.");
1074+
buildSrc("");
1075+
buildSrc(" WRITE 10 'a'.");
1076+
buildSrc(" WRITE 10(20) 'a'.");
1077+
buildSrc(" WRITE 10(*) 'a'.");
1078+
buildSrc(" WRITE 10(**) 'a'.");
1079+
1080+
putAnyMethodAroundSrc();
1081+
1082+
testParseCode();
1083+
}
9341084
}

0 commit comments

Comments
 (0)