Skip to content

Commit 68854cc

Browse files
authored
Merge pull request #2744 from guwirth/cpp23-escape-sequences
C++23: escape sequences
2 parents ef659c9 + 19728ba commit 68854cc

File tree

4 files changed

+114
-141
lines changed

4 files changed

+114
-141
lines changed

cxx-squid/dox/diff-cpp20-cpp23_grammar.txt

Lines changed: 0 additions & 119 deletions
This file was deleted.

cxx-squid/src/main/java/org/sonar/cxx/preprocessor/PPNumber.java

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@
2020
package org.sonar.cxx.preprocessor;
2121

2222
import java.math.BigInteger;
23+
import java.util.Collections;
2324
import java.util.HashMap;
25+
import java.util.Map;
26+
import org.sonar.api.internal.apachecommons.lang.StringUtils;
2427

2528
/**
2629
* Helper class to evaluate preprocessor numbers.
2730
*/
2831
final class PPNumber {
2932

3033
private static final HashMap<String, BigInteger> numberCache = new HashMap<>();
34+
private static final Map<String, Integer> namedUniversalCharacter = createNamedUniversalCharacter();
3135

3236
private PPNumber() {
3337

@@ -127,27 +131,79 @@ static BigInteger decodeCharacter(String charValue) {
127131
}
128132

129133
switch (charValue.charAt(1)) {
130-
case 't':
131-
return BigInteger.valueOf('\t');
132-
case 'b':
133-
return BigInteger.valueOf('\b');
134-
case 'n':
135-
return BigInteger.valueOf('\n');
136-
case 'r':
137-
return BigInteger.valueOf('\r');
138-
case 'f':
139-
return BigInteger.valueOf('\f');
140134
case '\'':
141135
return BigInteger.valueOf('\'');
142136
case '"':
143137
return BigInteger.valueOf('\"');
138+
case '?':
139+
return BigInteger.valueOf(0x3f);
144140
case '\\':
145141
return BigInteger.valueOf('\\');
142+
case 'a':
143+
return BigInteger.valueOf(0x07);
144+
case 'b':
145+
return BigInteger.valueOf('\b');
146+
case 'f':
147+
return BigInteger.valueOf('\f');
148+
case 'n':
149+
return BigInteger.valueOf('\n');
150+
case 'r':
151+
return BigInteger.valueOf('\r');
152+
case 't':
153+
return BigInteger.valueOf('\t');
154+
case 'v':
155+
return BigInteger.valueOf(0x0b);
156+
157+
case 'u':
158+
if (charValue.length() > 2 && charValue.charAt(2) == '{') {
159+
return delimitedEscapeSequences(charValue, 16);
160+
}
161+
return new BigInteger(StringUtils.substring(charValue, 2, 2 + 4), 16); // 4 hexadecimal digits
162+
163+
case 'U':
164+
return new BigInteger(StringUtils.substring(charValue, 2, 2 + 8), 16); // 8 hexadecimal digits
165+
146166
case 'x':
147-
case 'X':
167+
if (charValue.length() > 2 && charValue.charAt(2) == '{') {
168+
return delimitedEscapeSequences(charValue, 16);
169+
}
148170
return new BigInteger(charValue.substring(2), 16);
171+
172+
case 'o':
173+
if (charValue.length() > 2 && charValue.charAt(2) == '{') {
174+
return delimitedEscapeSequences(charValue, 8);
175+
}
176+
return BigInteger.ZERO;
177+
178+
case 'N':
179+
if (charValue.length() > 2 && charValue.charAt(2) == '{') {
180+
return delimitedEscapeSequences(charValue, -1);
181+
}
182+
return BigInteger.ZERO;
183+
149184
default:
150-
return new BigInteger(charValue.substring(1), 10);
185+
return new BigInteger(charValue.substring(1), 8);
151186
}
152187
}
188+
189+
static BigInteger delimitedEscapeSequences(String charValue, int radix) {
190+
int end = charValue.indexOf('}', 3);
191+
if (end != -1) {
192+
String value = charValue.substring(3, end);
193+
if (radix != -1) {
194+
return new BigInteger(value, radix);
195+
} else { // character named by NAME
196+
return BigInteger.valueOf(namedUniversalCharacter.getOrDefault(value, 1));
197+
}
198+
}
199+
return BigInteger.ZERO;
200+
}
201+
202+
// currently only NULL and NUL is supported, rest is mapped to 1
203+
private static Map<String, Integer> createNamedUniversalCharacter() {
204+
Map<String, Integer> result = new HashMap<>();
205+
result.put("NULL", 0);
206+
result.put("NUL", 0);
207+
return Collections.unmodifiableMap(result);
208+
}
153209
}

cxx-squid/src/test/java/org/sonar/cxx/preprocessor/PPNumberTest.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,34 @@ void decode_charcters() {
5959
assertThat(PPNumber.decodeCharacter("1")).isEqualTo(BigInteger.valueOf('1'));
6060
assertThat(PPNumber.decodeCharacter("A")).isEqualTo(BigInteger.valueOf('A'));
6161

62-
assertThat(PPNumber.decodeCharacter("\\t")).isEqualTo(BigInteger.valueOf('\t'));
63-
assertThat(PPNumber.decodeCharacter("\\b")).isEqualTo(BigInteger.valueOf('\b'));
64-
assertThat(PPNumber.decodeCharacter("\\n")).isEqualTo(BigInteger.valueOf('\n'));
65-
assertThat(PPNumber.decodeCharacter("\\r")).isEqualTo(BigInteger.valueOf('\r'));
66-
assertThat(PPNumber.decodeCharacter("\\f")).isEqualTo(BigInteger.valueOf('\f'));
62+
// simple escape sequences
6763
assertThat(PPNumber.decodeCharacter("\\'")).isEqualTo(BigInteger.valueOf('\''));
6864
assertThat(PPNumber.decodeCharacter("\\\"")).isEqualTo(BigInteger.valueOf('\"'));
65+
assertThat(PPNumber.decodeCharacter("\\?")).isEqualTo(BigInteger.valueOf(0x3f)); // \?
6966
assertThat(PPNumber.decodeCharacter("\\\\")).isEqualTo(BigInteger.valueOf('\\'));
67+
assertThat(PPNumber.decodeCharacter("\\a")).isEqualTo(BigInteger.valueOf(0x07)); // \a
68+
assertThat(PPNumber.decodeCharacter("\\b")).isEqualTo(BigInteger.valueOf('\b'));
69+
assertThat(PPNumber.decodeCharacter("\\f")).isEqualTo(BigInteger.valueOf('\f'));
70+
assertThat(PPNumber.decodeCharacter("\\n")).isEqualTo(BigInteger.valueOf('\n'));
71+
assertThat(PPNumber.decodeCharacter("\\r")).isEqualTo(BigInteger.valueOf('\r'));
72+
assertThat(PPNumber.decodeCharacter("\\t")).isEqualTo(BigInteger.valueOf('\t'));
73+
assertThat(PPNumber.decodeCharacter("\\v")).isEqualTo(BigInteger.valueOf(0x0b)); // \v
7074

75+
// numeric escape sequences
7176
assertThat(PPNumber.decodeCharacter("\\0")).isEqualTo(BigInteger.valueOf(0));
72-
assertThat(PPNumber.decodeCharacter("\\1")).isEqualTo(BigInteger.valueOf(1));
73-
77+
assertThat(PPNumber.decodeCharacter("\\123")).isEqualTo(BigInteger.valueOf(83));
78+
assertThat(PPNumber.decodeCharacter("\\o{123}")).isEqualTo(BigInteger.valueOf(83));
7479
assertThat(PPNumber.decodeCharacter("\\x00")).isEqualTo(BigInteger.valueOf(0));
75-
assertThat(PPNumber.decodeCharacter("\\x01")).isEqualTo(BigInteger.valueOf(1));
76-
assertThat(PPNumber.decodeCharacter("\\X00")).isEqualTo(BigInteger.valueOf(0));
77-
assertThat(PPNumber.decodeCharacter("\\X01")).isEqualTo(BigInteger.valueOf(1));
80+
assertThat(PPNumber.decodeCharacter("\\x0f")).isEqualTo(BigInteger.valueOf(15));
81+
assertThat(PPNumber.decodeCharacter("\\x{FF}")).isEqualTo(BigInteger.valueOf(255));
82+
83+
// universal character names
84+
assertThat(PPNumber.decodeCharacter("\\u12345")).isEqualTo(BigInteger.valueOf(0x1234));
85+
assertThat(PPNumber.decodeCharacter("\\U123456789")).isEqualTo(BigInteger.valueOf(0x12345678));
86+
assertThat(PPNumber.decodeCharacter("\\u{1234}")).isEqualTo(BigInteger.valueOf(0x1234));
87+
assertThat(PPNumber.decodeCharacter("\\N{NULL}")).isEqualTo(BigInteger.valueOf(0));
88+
assertThat(PPNumber.decodeCharacter("\\N{NUL}")).isEqualTo(BigInteger.valueOf(0));
89+
assertThat(PPNumber.decodeCharacter("\\N{NEW LINE}")).isEqualTo(BigInteger.valueOf(1));
7890
}
7991

8092
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// simple escape sequences
2+
auto s1 = '\'';
3+
auto s2 = '\"';
4+
auto s3 = '\?';
5+
auto s4 = '\\';
6+
auto s5 = '\a';
7+
auto s6 = '\b';
8+
auto s7 = '\f';
9+
auto s8 = '\n';
10+
auto s9 = '\r';
11+
auto s10 = '\t';
12+
auto s11 = '\v';
13+
14+
// numeric escape sequences
15+
auto n1 = '\123';
16+
auto n2 = '\o{12345}';
17+
auto n3 = '\xABCDEF';
18+
auto n4 = '\x{ABFF}';
19+
20+
// universal character names
21+
auto u1 = '\u1234';
22+
auto u2 = '\u{112233FF}';
23+
auto u3 = '\U12345678';
24+
auto u4 = '\N{NULL}';

0 commit comments

Comments
 (0)