Skip to content

Commit f903ec4

Browse files
committed
Round 4: operator new[], placement new, destructor call, alignas, requires, pack expansion
Parser: - operator new[] / operator delete[] array forms - placement new: new(buf) Type(args) - explicit destructor call: p->~Type() - alignas() specifier in declarations - trailing requires clause on member functions (IDENTIFIER check, not keyword) - Rest... pack expansion in function parameters - args... pack expansion in function call arguments - local struct/class definition in function body - inline namespace: preserve inline keyword - NamespaceDecl.isInline added to AST and CodeGen - parseTemplateDefaultValue: split >> when depth==1 - ::operator new / ::ScopedName in expressions - using X = Type in class/struct body - Deduction guide: Wrapper(const char*) -> Wrapper<std::string> - Constructor initializer list with qualified base names (std::runtime_error) - const volatile member functions - throw statement AstDecl: - NamespaceDecl.isInline field CppBuild: - Deduction guides deferred after struct definitions
1 parent a16d6bb commit f903ec4

5 files changed

Lines changed: 148 additions & 13 deletions

File tree

mode/CppMode.jar

1.35 KB
Binary file not shown.

src/java/AstDecl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ record EnumDecl(
207207

208208
record NamespaceDecl(
209209
String name,
210+
boolean isInline,
210211
List<TopLevelItem> items,
211212
int line,
212213
int col,

src/java/CodeGen.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ private static void emitEnumDecl(StringBuilder sb, EnumDecl e, int depth) {
147147

148148
private static void emitNamespaceDecl(StringBuilder sb, NamespaceDecl nd, int depth) {
149149
indent(sb, depth);
150+
if (nd.isInline()) sb.append("inline ");
150151
sb.append("namespace ").append(nd.name()).append(" {\n");
151152
for (TopLevelItem item : nd.items()) {
152153
emitTopLevelItem(sb, item, depth + 1);

src/java/CppBuild.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,10 @@ private File writeSketchImpl(RunnerListener listener) throws IOException {
12261226
} else if (item instanceof PreprocessorLine pl2) {
12271227
// Defer "using X = ..." type aliases (including template aliases) until after struct definitions
12281228
String rt = pl2.rawText().trim();
1229-
if ((rt.startsWith("using ") || rt.contains(" using ")) && !rt.contains("using namespace") && rt.contains("=")) {
1229+
// Defer "using X = ..." type aliases and deduction guides after struct definitions
1230+
boolean isTypeAlias = (rt.startsWith("using ") || rt.contains(" using ")) && !rt.contains("using namespace") && rt.contains("=");
1231+
boolean isDeductionGuide = rt.contains("->") && rt.contains("(") && !rt.startsWith("#");
1232+
if (isTypeAlias || isDeductionGuide) {
12301233
deferredAliases.add(item);
12311234
} else {
12321235
out.append(CodeGen.generateNode(item, 0));

src/java/Parser.java

Lines changed: 142 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
346346
if (checkKeyword("enum")) {
347347
return List.of(parseEnumDecl(leadingComments));
348348
}
349-
if (checkKeyword("namespace")) {
349+
if (checkKeyword("namespace") || (checkKeyword("inline") && pos + 1 < tokens.size() && tokens.get(pos + 1).isKeyword("namespace"))) {
350350
return List.of(parseNamespaceDecl(leadingComments));
351351
}
352352
// typedef -- emit verbatim as-is (covers function-pointer typedefs, etc.)
@@ -430,6 +430,25 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
430430
return List.of(parseFunctionOrConstructorOrDestructor(leadingComments, templateParams));
431431
}
432432

433+
// Deduction guide: "Wrapper(const char*) -> Wrapper<std::string>;"
434+
if (check(CppLexerTokenType.IDENTIFIER) && pos + 1 < tokens.size() && tokens.get(pos + 1).isPunct("(")) {
435+
int scan = pos + 2; int depth = 1;
436+
while (scan < tokens.size() && depth > 0) {
437+
if (tokens.get(scan).isPunct("(")) depth++;
438+
else if (tokens.get(scan).isPunct(")")) depth--;
439+
scan++;
440+
}
441+
if (scan < tokens.size() && tokens.get(scan).isPunct("->")) {
442+
int startPos = pos;
443+
while (!isAtEnd() && !checkPunct(";")) advance();
444+
matchPunct(";");
445+
StringBuilder raw = new StringBuilder();
446+
for (int i = startPos; i < pos; i++) { if (i > startPos) raw.append(" "); raw.append(tokens.get(i).text()); }
447+
raw.append(";");
448+
return List.of(new PreprocessorLine(raw.toString(), tokens.get(startPos).line(), tokens.get(startPos).col(), leadingComments));
449+
}
450+
}
451+
433452
return parseFunctionOrVariable(leadingComments, templateParams, true);
434453
}
435454

@@ -778,6 +797,15 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
778797
if (checkKeyword("enum")) {
779798
return List.of(parseEnumDecl(leadingComments));
780799
}
800+
// using X = Type; inside a struct/class body -- consume verbatim
801+
if (checkKeyword("using") && !tokens.get(pos + 1).isKeyword("namespace")) {
802+
int startPos = pos;
803+
{ int _d=0; while(!isAtEnd()){if(checkPunct("{"))_d++;else if(checkPunct("}")){if(_d==0){advance();break;}_d--;}else if(checkPunct(";")&&_d==0)break;advance();} matchPunct(";"); }
804+
StringBuilder raw = new StringBuilder();
805+
for (int i = startPos; i < pos - 1; i++) { if (i > startPos) raw.append(" "); raw.append(tokens.get(i).text()); }
806+
raw.append(";");
807+
return List.of(new PreprocessorLine(raw.toString(), tokens.get(startPos).line(), tokens.get(startPos).col(), leadingComments));
808+
}
781809
// Destructor dispatch: "~Name() {...}" or "virtual ~Name() {...}" --
782810
// must look one token past an optional leading "virtual", since that
783811
// keyword (confirmed common on destructors, e.g. "virtual ~LSystem()")
@@ -830,8 +858,7 @@ private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken>
830858
}
831859
}
832860

833-
// Consume trailing qualifiers: noexcept, noexcept(expr), override, final
834-
// These can appear after the param list in any order before "= 0" or body.
861+
// Consume trailing qualifiers: noexcept, noexcept(expr), override, final, requires
835862
while (true) {
836863
if (checkKeyword("noexcept")) {
837864
advance();
@@ -840,6 +867,10 @@ private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken>
840867
advance();
841868
} else if (check(CppLexerTokenType.IDENTIFIER) && peek().text().equals("final")) {
842869
advance();
870+
} else if (check(CppLexerTokenType.IDENTIFIER) && peek().text().equals("requires")) {
871+
advance();
872+
if (checkPunct("(")) { advance(); int d=1; while(!isAtEnd()&&d>0){if(checkPunct("("))d++;else if(checkPunct(")"))d--;advance();} }
873+
else { while (!isAtEnd() && !checkPunct("{") && !checkPunct(";") && !checkOp("=")) { if (checkOp("<")) { advance(); int d=1; while(!isAtEnd()&&d>0){if(checkOp("<"))d++;else if(checkOp(">"))d--;advance();} } else advance(); } }
843874
} else break;
844875
}
845876

@@ -875,7 +906,8 @@ private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken>
875906
}
876907

877908
private FunctionDecl.ConstructorInit parseConstructorInitEntry() {
878-
String memberName = expectIdentifier().text();
909+
// Base class names can be qualified: "std::runtime_error(msg)"
910+
String memberName = parseQualifiedTypeName();
879911
expectPunct("(");
880912
List<Expr> args = new ArrayList<>();
881913
if (!checkPunct(")")) {
@@ -972,8 +1004,9 @@ && looksLikeParamList()) {
9721004
boolean isOverride = false;
9731005
boolean isMethodConst = false;
9741006
// trailing const/override may appear in either order
975-
for (int i = 0; i < 2; i++) {
1007+
for (int i = 0; i < 3; i++) {
9761008
if (matchKeyword("const")) { isMethodConst = true; continue; }
1009+
if (matchKeyword("volatile")) { continue; } // const volatile method
9771010
if (matchKeyword("override")) { isOverride = true; continue; }
9781011
}
9791012
// Trailing return type: "auto f() -> ReturnType"
@@ -984,7 +1017,7 @@ && looksLikeParamList()) {
9841017
trailingReturnType = parseTypeRef();
9851018
}
9861019
TypeRef effectiveReturnType = (trailingReturnType != null) ? trailingReturnType : type;
987-
// Consume trailing qualifiers: noexcept, noexcept(expr), override, final
1020+
// Consume trailing qualifiers: noexcept, noexcept(expr), override, final, requires
9881021
while (true) {
9891022
if (checkKeyword("noexcept")) {
9901023
advance();
@@ -993,6 +1026,16 @@ && looksLikeParamList()) {
9931026
isOverride = true; advance();
9941027
} else if (check(CppLexerTokenType.IDENTIFIER) && peek().text().equals("final")) {
9951028
advance();
1029+
} else if (check(CppLexerTokenType.IDENTIFIER) && peek().text().equals("requires")) {
1030+
// trailing requires clause: "requires expr" -- consume to { or ;
1031+
advance();
1032+
if (checkPunct("(")) { advance(); int d=1; while(!isAtEnd()&&d>0){if(checkPunct("("))d++;else if(checkPunct(")"))d--;advance();} }
1033+
else { // bare expression like "requires std::is_arithmetic_v<T>"
1034+
while (!isAtEnd() && !checkPunct("{") && !checkPunct(";") && !checkOp("=")) {
1035+
if (checkOp("<")) { advance(); int d=1; while(!isAtEnd()&&d>0){if(checkOp("<"))d++;else if(checkOp(">"))d--;advance();} }
1036+
else advance();
1037+
}
1038+
}
9961039
} else break;
9971040
}
9981041
// Pure-virtual specifier ("= 0") or = default / = delete
@@ -1153,7 +1196,10 @@ private String parseFunctionOrVariableName() {
11531196
}
11541197
if (checkKeyword("new") || checkKeyword("delete")) {
11551198
CppLexerToken kw = advance();
1156-
return opKw.text() + " " + kw.text();
1199+
String opName = opKw.text() + " " + kw.text();
1200+
// operator new[] / operator delete[]
1201+
if (checkPunct("[")) { advance(); expectPunct("]"); opName += "[]"; }
1202+
return opName;
11571203
}
11581204
// Cast operator: "operator int", "operator float", "operator bool", etc.
11591205
// ONLY fire for actual C++ primitive type keywords, not arbitrary
@@ -1351,7 +1397,9 @@ private EnumDecl parseEnumDecl(List<CppLexerToken> leadingComments) {
13511397
}
13521398

13531399
private NamespaceDecl parseNamespaceDecl(List<CppLexerToken> leadingComments) {
1354-
CppLexerToken start = expectKeyword("namespace");
1400+
CppLexerToken start = peek();
1401+
boolean isInlineNs = matchKeyword("inline"); // "inline namespace"
1402+
expectKeyword("namespace");
13551403
StringBuilder nameBuilder = new StringBuilder(expectIdentifier().text());
13561404
while (checkPunct("::")) { advance(); nameBuilder.append("::").append(expectIdentifier().text()); }
13571405
String name = nameBuilder.toString();
@@ -1364,7 +1412,7 @@ private NamespaceDecl parseNamespaceDecl(List<CppLexerToken> leadingComments) {
13641412
items.addAll(parseTopLevelItem(comments));
13651413
}
13661414
expectPunct("}");
1367-
return new NamespaceDecl(name, items, start.line(), start.col(), leadingComments);
1415+
return new NamespaceDecl(name, isInlineNs, items, start.line(), start.col(), leadingComments);
13681416
}
13691417

13701418
private UsingNamespaceDecl parseUsingNamespaceDecl(List<CppLexerToken> leadingComments) {
@@ -1545,7 +1593,14 @@ else if (checkOp(">")) {
15451593
depth--; advance();
15461594
}
15471595
else if (checkOp(">>")) {
1548-
if (depth <= 1) break;
1596+
if (depth == 1) {
1597+
// Split ">>" into two ">" -- first closes inner template,
1598+
// second will close outer template param list
1599+
splitTrailingShiftIntoTwoCloseAngles();
1600+
depth--; advance(); // consume first ">"
1601+
break; // second ">" left for outer list
1602+
}
1603+
if (depth == 0) break;
15491604
depth -= 2; advance();
15501605
}
15511606
else if (depth == 0 && checkPunct(",")) break;
@@ -2021,6 +2076,14 @@ private Expr parseCast() {
20212076

20222077
private Expr parseNew() {
20232078
CppLexerToken start = expectKeyword("new");
2079+
// Placement new: "new (buf) Type(args)" -- consume placement args first
2080+
if (checkPunct("(")) {
2081+
advance(); int _pd=1;
2082+
while (!isAtEnd() && _pd > 0) {
2083+
if (checkPunct("(")) _pd++; else if (checkPunct(")")) _pd--;
2084+
advance();
2085+
}
2086+
}
20242087
TypeRef type = parseTypeRef();
20252088
if (matchPunct("[")) {
20262089
Expr sizeExpr = parseExpr();
@@ -2074,7 +2137,14 @@ private Expr parsePostfix() {
20742137
expr = new BinaryExpr(isArrow ? "->*" : ".*", expr, rhs, t.line(), t.col(), List.of());
20752138
continue;
20762139
}
2077-
String member = expectIdentifierOrKeywordName();
2140+
// Explicit destructor call: "p->~PoolObj()"
2141+
String member;
2142+
if (checkOp("~")) {
2143+
advance(); // consume ~
2144+
member = "~" + expectIdentifier().text();
2145+
} else {
2146+
member = expectIdentifierOrKeywordName();
2147+
}
20782148
// Sibling fix to the std::vector<int>(...) bug found via
20792149
// RealHeaderStressTest: a member name can ALSO be
20802150
// followed by explicit template arguments before the
@@ -2138,8 +2208,11 @@ private List<Expr> parseArgList() {
21382208
List<Expr> args = new ArrayList<>();
21392209
if (!checkPunct(")")) {
21402210
args.add(parseExpr());
2211+
matchPunct("..."); // pack expansion: "f(args...)"
21412212
while (matchPunct(",")) {
2213+
if (checkPunct(")")) break;
21422214
args.add(parseExpr());
2215+
matchPunct("..."); // pack expansion
21432216
}
21442217
}
21452218
expectPunct(")");
@@ -2219,6 +2292,19 @@ else if (checkPunct(",") && depth == 0) {
22192292
if (checkPunct("{")) {
22202293
return parseInitializerList();
22212294
}
2295+
// Global scope resolution: "::operator new(...)" or "::SomeFunc()"
2296+
if (checkPunct("::")) {
2297+
advance(); // consume ::
2298+
// Parse what follows as an identifier/operator name
2299+
if (checkKeyword("operator")) {
2300+
String opName = "::" + parseFunctionOrVariableName();
2301+
return new Identifier(opName, t.line(), t.col(), List.of());
2302+
}
2303+
if (check(CppLexerTokenType.IDENTIFIER)) {
2304+
String name = advance().text();
2305+
return new Identifier("::" + name, t.line(), t.col(), List.of());
2306+
}
2307+
}
22222308
// Wide/unicode string prefix: L"..." u"..." U"..." u8"..."
22232309
if (t.type() == CppLexerTokenType.IDENTIFIER
22242310
&& (t.text().equals("L") || t.text().equals("u") || t.text().equals("U") || t.text().equals("u8"))
@@ -2588,6 +2674,7 @@ private Param parseParam() {
25882674
}
25892675

25902676
TypeRef type = parseTypeRef();
2677+
matchPunct("..."); // pack expansion after type: "Rest... rest"
25912678

25922679
// Member pointer parameter: "int Widget::* dp" or "int (Widget::* mp)(int) const"
25932680
if (check(CppLexerTokenType.IDENTIFIER) && pos + 1 < tokens.size()
@@ -2767,6 +2854,19 @@ private Block parseBlock() {
27672854
*/
27682855
private List<Statement> parseStatementOrMultiDecl(List<CppLexerToken> leadingComments) {
27692856
if (isStructuredBindingStart()) return List.of(parseStructuredBinding(leadingComments));
2857+
// Local struct/class definition: "struct Point { float x, y; }; Point p{...}"
2858+
if ((checkKeyword("struct") || checkKeyword("class"))
2859+
&& pos + 1 < tokens.size()
2860+
&& (tokens.get(pos + 1).type() == CppLexerTokenType.IDENTIFIER
2861+
|| tokens.get(pos + 1).isPunct("{"))) {
2862+
TypeDef td = parseTypeDef(leadingComments, List.of());
2863+
matchPunct(";");
2864+
// If a variable declaration follows, parse it
2865+
if (looksLikeDeclaration()) {
2866+
return new java.util.ArrayList<>(parseDeclStatementsDesugared(List.of()));
2867+
}
2868+
return List.of(new ExprStatement(new Identifier("", td.line(), td.col(), List.of()), td.line(), td.col(), leadingComments));
2869+
}
27702870
if (looksLikeDeclaration()) {
27712871
return new ArrayList<Statement>(parseDeclStatementsDesugared(leadingComments));
27722872
}
@@ -2812,6 +2912,25 @@ private Statement parseStatement(List<CppLexerToken> leadingComments) {
28122912
CppLexerToken t0 = tokens.get(startPos);
28132913
return new ExprStatement(new Identifier(raw.toString(), t0.line(), t0.col(), List.of()), t0.line(), t0.col(), leadingComments);
28142914
}
2915+
// throw statement: "throw expr;" or bare "throw;"
2916+
if (checkKeyword("throw")) {
2917+
CppLexerToken t0 = advance();
2918+
if (checkPunct(";")) { advance(); return new ReturnStatement(null, t0.line(), t0.col(), leadingComments); }
2919+
Expr val = parseExpr();
2920+
expectPunct(";");
2921+
// Encode as ReturnStatement with a UnaryExpr("throw", val) -- CodeGen emits via renderExpr
2922+
return new ExprStatement(new UnaryExpr("throw ", val, t0.line(), t0.col(), List.of()), t0.line(), t0.col(), leadingComments);
2923+
}
2924+
2925+
// Local struct/class definition inside a function body
2926+
if ((checkKeyword("struct") || checkKeyword("class"))
2927+
&& pos + 1 < tokens.size()
2928+
&& (tokens.get(pos + 1).type() == CppLexerTokenType.IDENTIFIER
2929+
|| tokens.get(pos + 1).isPunct("{"))) {
2930+
TypeDef td = parseTypeDef(leadingComments, List.of());
2931+
matchPunct(";");
2932+
return new ExprStatement(new Identifier("", td.line(), td.col(), List.of()), td.line(), td.col(), leadingComments);
2933+
}
28152934
if (checkKeyword("if")) return parseIf(leadingComments);
28162935
if (checkKeyword("for")) return parseForOrRangeFor(leadingComments);
28172936
if (checkKeyword("while")) return parseWhile(leadingComments);
@@ -3150,7 +3269,13 @@ private boolean looksLikeDeclaration() {
31503269
if (checkKeyword("static")) advance();
31513270
if (checkKeyword("volatile")) advance();
31523271
if (checkKeyword("const")) advance();
3153-
if (checkKeyword("volatile")) advance(); // volatile after const
3272+
if (checkKeyword("volatile")) advance(); // vol
3273+
// alignas specifier: "alignas(T) type name"
3274+
if (checkKeyword("alignas") && pos + 1 < tokens.size() && tokens.get(pos + 1).isPunct("(")) {
3275+
advance(); advance(); int _ad=1;
3276+
while (!isAtEnd() && _ad > 0) { if (checkPunct("(")) _ad++; else if (checkPunct(")")) _ad--; advance(); }
3277+
}
3278+
// volatile after const
31543279
if (checkKeyword("constexpr")) advance();
31553280
if (checkKeyword("inline")) advance();
31563281
if (checkKeyword("static")) advance(); // tolerate either order
@@ -3200,6 +3325,11 @@ private List<DeclStatement> parseDeclStatementsDesugared(List<CppLexerToken> lea
32003325
// statement, then "int" showed up where a ";" was expected).
32013326
// Tolerate either order ("static const" or "const static"),
32023327
// mirroring the top-level path's own tolerance.
3328+
// alignas specifier: "alignas(T) type name"
3329+
if (checkKeyword("alignas") && pos + 1 < tokens.size() && tokens.get(pos + 1).isPunct("(")) {
3330+
advance(); advance(); int _ad=1;
3331+
while (!isAtEnd() && _ad > 0) { if (checkPunct("(")) _ad++; else if (checkPunct(")")) _ad--; advance(); }
3332+
}
32033333
boolean isStatic = matchKeyword("static");
32043334
matchKeyword("volatile"); // consume volatile qualifier at statement scope
32053335
boolean isConst = matchKeyword("const");

0 commit comments

Comments
 (0)