Skip to content

Commit 1d5595e

Browse files
committed
Round 14: binary literals, constexpr constructors, concept ordering, friend templates
Parser: - constexpr on constructors: parseFunctionOrConstructorOrDestructor consumes constexpr and rebuilds FunctionDecl with isConstexpr=true - Friend function templates: pass outer templateParams to friend handler so template<typename U> before friend is correctly associated - sizeof...(Ts) in template args: full expression preserved in NamedType CodeGen: - Constructor init list: use braces {} for InitializerListExpr args (m{{r0},{r1}}) - Friend binary operators with template params: emit template<> prefix before friend CppBuild: - Concept deduction guide check: concepts with requires { } -> not misclassified as deduction guides (was deferring Printable concept to after struct Sketch) CppLexer: - Binary integer literals: 0b1010, 0b1100, 0b10101000 now lexed as INT_LITERAL
1 parent 67e858d commit 1d5595e

5 files changed

Lines changed: 68 additions & 16 deletions

File tree

mode/CppMode.jar

818 Bytes
Binary file not shown.

src/java/CodeGen.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,24 @@ private static void emitTypeDef(StringBuilder sb, TypeDef td, int depth) {
197197
&& !fd.name().equals("operator[]")
198198
&& !fd.name().equals("operator()")) {
199199
indent(sb, depth + 1);
200+
// Template prefix must come before "friend"
201+
// Build a stripped FunctionDecl (no templateParams) to avoid double-emission
202+
FunctionDecl fdStripped = fd;
203+
if (!fd.templateParams().isEmpty()) {
204+
sb.append("template<");
205+
for (int _i = 0; _i < fd.templateParams().size(); _i++) {
206+
if (_i > 0) sb.append(", ");
207+
sb.append(fd.templateParams().get(_i));
208+
}
209+
sb.append(">\n");
210+
indent(sb, depth + 1);
211+
fdStripped = new FunctionDecl(fd.returnType(), fd.name(), List.of(), fd.params(),
212+
fd.initializerList(), fd.body(), fd.isConstructor(), fd.isDestructor(),
213+
fd.isVirtual(), fd.isOverride(), fd.isConst(), fd.isConstexpr(), fd.isStatic(),
214+
fd.isPureVirtual(), fd.isDefault(), fd.isDelete(), fd.line(), fd.col(), fd.leadingComments());
215+
}
200216
sb.append("friend ");
201-
emitFunctionDecl(sb, fd, 0);
217+
emitFunctionDecl(sb, fdStripped, 0);
202218
continue;
203219
}
204220
emitTopLevelItem(sb, member, depth + 1);
@@ -530,12 +546,19 @@ private static void emitFunctionDecl(StringBuilder sb, FunctionDecl fd, int dept
530546
String mname = e.memberName();
531547
boolean isPack = mname.endsWith("...");
532548
if (isPack) mname = mname.substring(0, mname.length() - 3);
533-
sb.append(mname).append('(');
534-
for (int j = 0; j < e.args().size(); j++) {
535-
if (j > 0) sb.append(", ");
536-
sb.append(renderExpr(e.args().get(j)));
549+
// Use braces for nested brace-init (arrays, structs): "m{{r0},{r1},{r2}}"
550+
boolean usesBraces = e.args().size() == 1 && e.args().get(0) instanceof InitializerListExpr;
551+
if (usesBraces) {
552+
// InitializerListExpr already renders with {}, just append directly
553+
sb.append(mname).append(renderExpr(e.args().get(0)));
554+
} else {
555+
sb.append(mname).append('(');
556+
for (int j = 0; j < e.args().size(); j++) {
557+
if (j > 0) sb.append(", ");
558+
sb.append(renderExpr(e.args().get(j)));
559+
}
560+
sb.append(')');
537561
}
538-
sb.append(')');
539562
if (isPack) sb.append("...");
540563
}
541564
}

src/java/CppBuild.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1228,7 +1228,10 @@ private File writeSketchImpl(RunnerListener listener) throws IOException {
12281228
String rt = pl2.rawText().trim();
12291229
// Defer "using X = ..." type aliases and deduction guides after struct definitions
12301230
boolean isTypeAlias = (rt.startsWith("using ") || rt.contains(" using ")) && !rt.contains("using namespace") && rt.contains("=");
1231-
boolean isDeductionGuide = rt.contains("->") && rt.contains("(") && !rt.startsWith("#");
1231+
// Deduction guide: "Name(params) -> Name<params>;" -- NOT concepts with requires { } ->
1232+
// Distinguish by checking -> appears before any { (deduction guides have no braces)
1233+
boolean isDeductionGuide = rt.contains("->") && rt.contains("(") && !rt.startsWith("#")
1234+
&& !rt.contains("concept") && !rt.contains("requires") && (rt.indexOf("{") < 0 || rt.indexOf("->") < rt.indexOf("{"));
12321235
if (isTypeAlias || isDeductionGuide) {
12331236
deferredAliases.add(item);
12341237
} else if (rt.startsWith("template ") && !rt.startsWith("template<") && !rt.startsWith("template <")) {

src/java/CppLexer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ private CppLexerToken lexNumericLiteral(int startLine, int startCol) {
235235
consumeIntSuffix(sb);
236236
return new CppLexerToken(CppLexerTokenType.INT_LITERAL, sb.toString(), startLine, startCol);
237237
}
238+
// Binary literal: 0b...
239+
if (peekChar() == '0' && (peekChar(1) == 'b' || peekChar(1) == 'B')) {
240+
sb.append(advanceChar());
241+
sb.append(advanceChar());
242+
while (pos < len && (peekChar() == '0' || peekChar() == '1' || peekChar() == '_')) {
243+
sb.append(advanceChar());
244+
}
245+
consumeIntSuffix(sb);
246+
return new CppLexerToken(CppLexerTokenType.INT_LITERAL, sb.toString(), startLine, startCol);
247+
}
238248

239249
boolean isFloat = false;
240250
while (pos < len && Character.isDigit(peekChar())) {

src/java/Parser.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,8 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
928928
return List.of(new PreprocessorLine("// friend class", tokens.get(startPos).line(), tokens.get(startPos).col(), leadingComments));
929929
}
930930
// "friend RetType funcName(...)" or "friend RetType operator...()" -- parse as function
931-
// Consume template params if present
932-
List<String> friendTemplateParams = List.of();
931+
// Consume template params if present; also use outer templateParams if already consumed
932+
List<String> friendTemplateParams = templateParams.isEmpty() ? List.of() : templateParams;
933933
if (matchKeyword("template")) friendTemplateParams = parseTemplateParamList();
934934
return parseFunctionOrVariable(leadingComments, friendTemplateParams, false);
935935
}
@@ -952,7 +952,16 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
952952
// Constructor: match enclosingClassName OR just its base name (for specializations like Grid<T,N,false>)
953953
String enclosingBase = enclosingClassName.contains("<") ? enclosingClassName.substring(0, enclosingClassName.indexOf("<")) : enclosingClassName;
954954
if (check(CppLexerTokenType.IDENTIFIER) && (peek().text().equals(enclosingClassName) || peek().text().equals(enclosingBase)) && tokens.get(pos + 1).isPunct("(")) {
955-
return List.of(parseFunctionOrConstructorOrDestructor(leadingComments, templateParams));
955+
FunctionDecl ctorFd = parseFunctionOrConstructorOrDestructor(leadingComments, templateParams);
956+
if (isConstexprCtor && !ctorFd.isConstexpr()) {
957+
// constexpr was consumed before dispatch; rebuild FunctionDecl with isConstexpr=true
958+
ctorFd = new FunctionDecl(ctorFd.returnType(), ctorFd.name(), ctorFd.templateParams(),
959+
ctorFd.params(), ctorFd.initializerList(), ctorFd.body(),
960+
ctorFd.isConstructor(), ctorFd.isDestructor(), ctorFd.isVirtual(), ctorFd.isOverride(),
961+
ctorFd.isConst(), true, ctorFd.isStatic(), ctorFd.isPureVirtual(), ctorFd.isDefault(), ctorFd.isDelete(),
962+
ctorFd.line(), ctorFd.col(), ctorFd.leadingComments());
963+
}
964+
return List.of(ctorFd);
956965
}
957966
return parseFunctionOrVariable(leadingComments, templateParams, false);
958967
}
@@ -966,6 +975,7 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
966975
private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken> leadingComments, List<String> templateParams) {
967976
CppLexerToken start = peek();
968977
boolean isVirtual = matchKeyword("virtual");
978+
boolean isConstexprCtorMethod = matchKeyword("constexpr") || matchKeyword("consteval");
969979
boolean isDestructor = matchOp("~");
970980
String name = expectIdentifier().text();
971981
if (isDestructor) name = "~" + name;
@@ -1032,7 +1042,7 @@ private FunctionDecl parseFunctionOrConstructorOrDestructor(List<CppLexerToken>
10321042
else if (isPureVirtual || isDefault || isDelete) expectPunct(";");
10331043

10341044
return new FunctionDecl(null, name, templateParams, params, initList, body,
1035-
!isDestructor, isDestructor, isVirtual, isOverride, isConst, false, false, isPureVirtual, isDefault, isDelete,
1045+
!isDestructor, isDestructor, isVirtual, isOverride, isConst, false, isConstexprCtorMethod, isPureVirtual, isDefault, isDelete,
10361046
start.line(), start.col(), leadingComments);
10371047
}
10381048

@@ -1929,13 +1939,19 @@ private TypeRef parseTemplateArg() {
19291939
}
19301940
// sizeof expression: sizeof(T) or sizeof...(Args)
19311941
if (checkKeyword("sizeof")) {
1932-
int save = pos; advance();
1933-
if (checkPunct("...")) advance();
1942+
int sStart = pos; advance();
1943+
StringBuilder sof = new StringBuilder("sizeof");
1944+
if (checkPunct("...")) { sof.append("..."); advance(); }
19341945
if (checkPunct("(")) {
1935-
advance(); int d = 1;
1936-
while (!isAtEnd() && d > 0) { if (checkPunct("(")) d++; else if (checkPunct(")")) d--; advance(); }
1946+
sof.append("("); advance(); int d = 1;
1947+
while (!isAtEnd() && d > 0) {
1948+
if (checkPunct("(")) { d++; sof.append("("); advance(); }
1949+
else if (checkPunct(")")) { d--; if (d > 0) sof.append(")"); advance(); }
1950+
else { sof.append(peek().text()); advance(); }
1951+
}
1952+
sof.append(")");
19371953
}
1938-
return new NamedType("sizeof(...)", List.of(), 0, false, false, false);
1954+
return new NamedType(sof.toString(), List.of(), 0, false, false, false);
19391955
}
19401956
TypeRef maybeReturnType = parseTypeRef();
19411957
// Compound boolean expression in template arg: "is_arithmetic_v<T> && !is_same_v<T,bool>"

0 commit comments

Comments
 (0)