Skip to content

Commit 3fe0117

Browse files
committed
Round 6: peek() fix, consteval/constinit, explicit template instantiation, bit fields
Critical bug fix: - peek() was mutating pos as side effect of skipCommentsAt(), causing checkKeyword/checkOp to see wrong tokens when comments preceded them. Fixed peek() to scan without mutating pos. Parser: - consteval and constinit added to keyword set and qualifier loops - matchKeyword(template) in parseTopLevelItem before checking for so checkOp('<') sees correct token after template is consumed - Explicit template instantiation: template class Foo<int>; consumed verbatim - Concept-constrained template param: Numeric T in template<Numeric T> - Global structured binding: auto [a, b] = expr; at top level - Bit field declarations: unsigned int x : 1; consumed - __attribute__((...)): consumed in trailing qualifier position AstDecl: - FunctionDecl.isDefault and FunctionDecl.isDelete fields - CodeGen emits = default and = delete correctly
1 parent 1aa51ca commit 3fe0117

7 files changed

Lines changed: 74 additions & 20 deletions

File tree

mode/CppMode.jar

830 Bytes
Binary file not shown.

src/java/AstDecl.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ record FunctionDecl(
118118
boolean isConst,
119119
boolean isConstexpr,
120120
boolean isStatic,
121-
boolean isPureVirtual, // "= 0" specifier -- distinct from body==null (an ordinary forward
122-
// declaration also has a null body but should NOT render "= 0")
121+
boolean isPureVirtual, // "= 0" specifier
122+
boolean isDefault, // "= default"
123+
boolean isDelete, // "= delete"
123124
int line,
124125
int col,
125126
List<CppLexerToken> leadingComments

src/java/AstPasses.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ public static List<FunctionDecl> generate(List<FunctionDecl> hoistedFunctions) {
655655
fd.returnType(), fd.name(), fd.templateParams(), fd.params(),
656656
fd.initializerList(), null /* body -- this is what makes it a forward decl */,
657657
fd.isConstructor(), fd.isDestructor(), fd.isVirtual(), fd.isOverride(),
658-
fd.isConst(), fd.isConstexpr(), fd.isStatic(), false /* isPureVirtual -- never applies to a hoisted free function */,
658+
fd.isConst(), fd.isConstexpr(), fd.isStatic(), false /* isPureVirtual */, false /* isDefault */, false /* isDelete */,
659659
fd.line(), fd.col(), List.of() /* no comments on the forward decl */
660660
));
661661
}

src/java/CodeGen.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,18 @@ private static void emitFunctionDecl(StringBuilder sb, FunctionDecl fd, int dept
496496
&& fd.returnType() instanceof NamedType nt
497497
&& nt.baseName().equals("operator")
498498
&& nt.pointerDepth() == 0;
499-
if (!isCastOp) {
499+
// Trailing return type: decltype(...) must use "auto name(params) -> decltype(...)"
500+
boolean isTrailing = !isCastOp && fd.returnType() instanceof NamedType ntr2
501+
&& ntr2.baseName().startsWith("decltype");
502+
if (!isCastOp && !isTrailing) {
500503
sb.append(renderTypeRef(fd.returnType())).append(' ');
504+
} else if (isTrailing) {
505+
sb.append("auto ");
501506
}
502507
sb.append(fd.name()).append('(');
503508
emitParamList(sb, fd.params());
504509
sb.append(')');
510+
if (isTrailing) sb.append(" -> ").append(renderTypeRef(fd.returnType()));
505511
}
506512

507513
if (fd.isConst()) sb.append(" const");
@@ -528,6 +534,8 @@ private static void emitFunctionDecl(StringBuilder sb, FunctionDecl fd, int dept
528534

529535
if (fd.body() == null) {
530536
if (fd.isPureVirtual()) sb.append(" = 0");
537+
else if (fd.isDefault()) sb.append(" = default");
538+
else if (fd.isDelete()) sb.append(" = delete");
531539
else if (fd.isConst() && fd.name().contains("<=>")
532540
&& fd.returnType() instanceof NamedType nt && nt.baseName().equals("auto")) {
533541
sb.append(" = default"); // auto operator<=> = default

src/java/CppLexer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public final class CppLexer {
2929
private static final Set<String> KEYWORDS = Set.of(
3030
"alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor",
3131
"bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class",
32-
"compl", "const", "constexpr", "const_cast", "continue", "decltype",
32+
"compl", "const", "consteval", "constinit", "constexpr", "const_cast", "continue", "decltype",
3333
"default", "delete", "do", "double", "dynamic_cast", "else", "enum",
3434
"explicit", "export", "extern", "false", "float", "for", "friend", "goto",
3535
"if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept",

src/java/LifecycleRewriter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static TopLevelItem rewriteTopLevelItem(TopLevelItem item, Set<String> l
9696
return new FunctionDecl(
9797
fd.returnType(), fd.name(), fd.templateParams(), fd.params(), fd.initializerList(),
9898
newBody, fd.isConstructor(), fd.isDestructor(), fd.isVirtual(),
99-
fd.isOverride() || shouldOverride, fd.isConst(), fd.isConstexpr(), fd.isStatic(), fd.isPureVirtual(),
99+
fd.isOverride() || shouldOverride, fd.isConst(), fd.isConstexpr(), fd.isStatic(), fd.isPureVirtual(), fd.isDefault(), fd.isDelete(),
100100
fd.line(), fd.col(), fd.leadingComments()
101101
);
102102
}
@@ -124,7 +124,7 @@ private static TopLevelItem rewriteClassMember(TopLevelItem member) {
124124
return new FunctionDecl(
125125
fd.returnType(), fd.name(), fd.templateParams(), fd.params(), fd.initializerList(),
126126
newBody, fd.isConstructor(), fd.isDestructor(), fd.isVirtual(),
127-
fd.isOverride(), fd.isConst(), fd.isConstexpr(), fd.isStatic(), fd.isPureVirtual(),
127+
fd.isOverride(), fd.isConst(), fd.isConstexpr(), fd.isStatic(), fd.isPureVirtual(), fd.isDefault(), fd.isDelete(),
128128
fd.line(), fd.col(), fd.leadingComments()
129129
);
130130
}

src/java/Parser.java

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ private void skipCommentsAt() {
7474
}
7575

7676
private CppLexerToken peek() {
77-
skipCommentsAt();
78-
return tokens.get(pos);
77+
int p = pos;
78+
while (p < tokens.size() && (tokens.get(p).type() == CppLexerTokenType.LINE_COMMENT || tokens.get(p).type() == CppLexerTokenType.BLOCK_COMMENT)) p++;
79+
return p < tokens.size() ? tokens.get(p) : tokens.get(tokens.size() - 1);
7980
}
8081

8182
private CppLexerToken peek(int ahead) {
@@ -276,7 +277,17 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
276277
}
277278

278279
List<String> templateParams = List.of();
279-
if (checkKeyword("template")) {
280+
if (matchKeyword("template")) {
281+
// Explicit template instantiation: "template class Foo<int>;" (no < after template)
282+
if (!checkOp("<")) {
283+
int startPos = pos - 1;
284+
while (!isAtEnd() && !checkPunct(";")) advance();
285+
matchPunct(";");
286+
StringBuilder raw = new StringBuilder();
287+
for (int i = startPos; i < pos - 1; i++) { if (i > startPos) raw.append(" "); raw.append(tokens.get(i).text()); }
288+
raw.append(";");
289+
return List.of(new PreprocessorLine(raw.toString(), tokens.get(startPos).line(), tokens.get(startPos).col(), leadingComments));
290+
}
280291
templateParams = parseTemplateParamList();
281292
consumeLeadingComments();
282293

@@ -430,6 +441,11 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
430441
return List.of(parseFunctionOrConstructorOrDestructor(leadingComments, templateParams));
431442
}
432443

444+
// Global structured binding: "auto [a, b] = expr;"
445+
if (isStructuredBindingStart()) {
446+
Statement sb2 = parseStructuredBinding(leadingComments);
447+
return List.of(new TopLevelStatement(sb2, sb2.line(), sb2.col(), leadingComments));
448+
}
433449
// Deduction guide: "Wrapper(const char*) -> Wrapper<std::string>;"
434450
if (check(CppLexerTokenType.IDENTIFIER) && pos + 1 < tokens.size() && tokens.get(pos + 1).isPunct("(")) {
435451
int scan = pos + 2; int depth = 1;
@@ -454,7 +470,8 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
454470

455471
/** "template<typename T, typename U, ...>" -- returns just the param names. */
456472
private List<String> parseTemplateParamList() {
457-
expectKeyword("template");
473+
// "template" already consumed by the caller; just consume "<...>"
474+
if (checkKeyword("template")) advance(); // in case called with template still present
458475
expectOp("<");
459476
List<String> params = new ArrayList<>();
460477
// template<> is a valid explicit full specialization marker -- emit as empty list
@@ -558,6 +575,20 @@ private String parseTemplateParamName() {
558575
return "<template-template>";
559576
}
560577

578+
// --- concept-constrained type parameter: "Numeric T" ---
579+
if (check(CppLexerTokenType.IDENTIFIER) && pos + 1 < tokens.size()
580+
&& tokens.get(pos + 1).type() == CppLexerTokenType.IDENTIFIER
581+
&& pos + 2 < tokens.size()
582+
&& (tokens.get(pos + 2).isOp(">") || tokens.get(pos + 2).isOp(">>")
583+
|| tokens.get(pos + 2).isPunct(",")
584+
|| tokens.get(pos + 2).isPunct("...")
585+
|| tokens.get(pos + 2).isOp("="))) {
586+
advance();
587+
if (checkPunct("...")) advance();
588+
String name = advance().text();
589+
if (matchOp("=")) parseTemplateDefaultValue();
590+
return name;
591+
}
561592
// --- typename / class type parameter ---
562593
if (checkKeyword("typename") || checkKeyword("class")) {
563594
advance(); // consume typename/class
@@ -886,7 +917,7 @@ private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken>
886917
// using "virtual void draw() = 0;" on an abstract base class --
887918
// confirmed real, ordinary OOP, not exotic. Must be checked
888919
// BEFORE the body/";" check below, since "= 0;" replaces both.
889-
boolean isPureVirtual = false;
920+
boolean isPureVirtual = false, isDefault = false, isDelete = false;
890921
if (checkOp("=")) {
891922
int save = pos;
892923
advance();
@@ -903,11 +934,11 @@ private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken>
903934
}
904935

905936
Block body = checkPunct("{") ? parseBlock() : null;
906-
if (body == null && !isPureVirtual) expectPunct(";");
907-
else if (isPureVirtual) expectPunct(";");
937+
if (body == null && !isPureVirtual && !isDefault && !isDelete) expectPunct(";");
938+
else if (isPureVirtual || isDefault || isDelete) expectPunct(";");
908939

909940
return new FunctionDecl(null, name, templateParams, params, initList, body,
910-
!isDestructor, isDestructor, isVirtual, isOverride, isConst, false, false, isPureVirtual,
941+
!isDestructor, isDestructor, isVirtual, isOverride, isConst, false, false, isPureVirtual, isDefault, isDelete,
911942
start.line(), start.col(), leadingComments);
912943
}
913944

@@ -974,7 +1005,8 @@ private List<TopLevelItem> parseFunctionOrVariable(List<CppLexerToken> leadingCo
9741005
matchKeyword("inline");
9751006
matchKeyword("volatile"); // consume volatile qualifier
9761007
boolean isConst = matchKeyword("const");
977-
boolean isConstexprFn = matchKeyword("constexpr"); if (isConstexprFn) isConst = true;
1008+
boolean isConstexprFn = matchKeyword("constexpr") || matchKeyword("consteval"); if (isConstexprFn) isConst = true;
1009+
matchKeyword("constinit");
9781010
if (!isVirtual) isVirtual = matchKeyword("virtual"); // constexpr virtual
9791011
if (!isStatic) isStatic = matchKeyword("static");
9801012
if (!isConst) isConst = matchKeyword("const");
@@ -1024,6 +1056,11 @@ && looksLikeParamList()) {
10241056
trailingReturnType = parseTypeRef();
10251057
}
10261058
TypeRef effectiveReturnType = (trailingReturnType != null) ? trailingReturnType : type;
1059+
// Consume __attribute__((...)): GCC attribute syntax before trailing qualifiers
1060+
if (check(CppLexerTokenType.IDENTIFIER) && peek().text().equals("__attribute__")) {
1061+
advance(); // __attribute__
1062+
if (checkPunct("(")) { advance(); int _ad=1; while(!isAtEnd()&&_ad>0){if(checkPunct("("))_ad++;else if(checkPunct(")"))_ad--;advance();} }
1063+
}
10271064
// Consume trailing qualifiers: noexcept, noexcept(expr), override, final, requires
10281065
while (true) {
10291066
if (checkKeyword("noexcept")) {
@@ -1046,14 +1083,16 @@ && looksLikeParamList()) {
10461083
} else break;
10471084
}
10481085
// Pure-virtual specifier ("= 0") or = default / = delete
1049-
boolean isPureVirtual = false;
1086+
boolean isPureVirtual = false, isDefault = false, isDelete = false;
10501087
if (checkOp("=")) {
10511088
int save = pos;
10521089
advance();
10531090
if (checkLiteralZero()) {
10541091
advance(); isPureVirtual = true;
1055-
} else if (checkKeyword("default") || checkKeyword("delete")) {
1056-
advance(); // = default or = delete
1092+
} else if (checkKeyword("default")) {
1093+
advance(); isDefault = true;
1094+
} else if (checkKeyword("delete")) {
1095+
advance(); isDelete = true;
10571096
} else {
10581097
pos = save;
10591098
}
@@ -1073,12 +1112,16 @@ && looksLikeParamList()) {
10731112
Block body = checkPunct("{") ? parseBlock() : null;
10741113
if (body == null) expectPunct(";");
10751114
FunctionDecl fn = new FunctionDecl(effectiveReturnType, name, templateParams, params, List.of(), body,
1076-
false, false, isVirtual, isOverride, isMethodConst, isConstexprFn, isStatic, isPureVirtual,
1115+
false, false, isVirtual, isOverride, isMethodConst, isConstexprFn, isStatic, isPureVirtual, isDefault, isDelete,
10771116
start.line(), start.col(), leadingComments);
10781117
return List.of(fn);
10791118
}
10801119

10811120
// Variable declaration, possibly multi-declarator.
1121+
// Bit field: "unsigned int read : 1" -- consume ": N" width specifier
1122+
if (checkPunct(":") && pos + 1 < tokens.size() && tokens.get(pos + 1).type() == CppLexerTokenType.INT_LITERAL) {
1123+
advance(); advance(); // consume ":" and bit width
1124+
}
10821125
List<TopLevelItem> result = new ArrayList<>();
10831126
result.add(parseOneTopLevelDeclarator(type, name, isConst, isStatic, templateParams, start, leadingComments));
10841127
while (matchPunct(",")) {
@@ -1142,6 +1185,8 @@ private boolean looksLikeTopLevelDeclarationOrFunction() {
11421185
matchKeyword("volatile");
11431186
matchKeyword("const");
11441187
matchKeyword("constexpr");
1188+
matchKeyword("consteval");
1189+
matchKeyword("constinit");
11451190
matchKeyword("inline");
11461191
matchKeyword("static"); // tolerate either order, mirroring the real parse path
11471192

0 commit comments

Comments
 (0)