Skip to content

Commit 5ef1c34

Browse files
committed
Round 9: extern C++, explicit(), friend, alignas struct, local fwd decl, lambda default tmpl arg
Parser: - extern "C++" / extern "C" linkage specifications consumed and parsed - explicit(...) conditional explicit in struct member (C++20) - friend declaration/definition consumed verbatim in parseClassMember - alignas(...) before struct/class name in parseTypeDef - [[attributes]] consumed before struct/class name - Local struct forward declaration: struct Foo; inside function body - Specialization args in parseTypeDef: manual depth-tracked scan not parseTemplateArgList - Constructor name matching: compare base name for specializations Grid<T,N,false> - parseTemplateDefaultValue: track {} depth so < inside lambdas doesn't affect angle depth CppBuild: - preprocessMacros: filter out system header content from g++ -E output, keeping only tokens originating from the sketch file itself
1 parent b06c9c2 commit 5ef1c34

3 files changed

Lines changed: 101 additions & 17 deletions

File tree

mode/CppMode.jar

887 Bytes
Binary file not shown.

src/java/CppBuild.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2899,16 +2899,27 @@ private String preprocessMacros(String code) {
28992899
return code;
29002900
}
29012901

2902-
// g++ -E emits "# <line> \"<file>\" <flags>" GNU-style line markers
2903-
// (or "#line <n> \"<file>\"" depending on version). Strip any line
2904-
// beginning with "# " followed by a digit, or "#line", since CppBuild's
2905-
// downstream scanners don't need them and they'd otherwise be mistaken
2906-
// for ordinary text by the brace/class scanners.
2902+
// g++ -E emits "# <line> \"<file>\" <flags>" GNU-style line markers.
2903+
// We keep ONLY lines that originate from the scratch file itself,
2904+
// filtering out system header expansions (#include <type_traits> etc.)
2905+
// which would overwhelm the CppMode parser with STL internals.
2906+
String scratchPath = scratch.getAbsolutePath();
29072907
StringBuilder cleaned = new StringBuilder();
2908+
boolean inScratchFile = true; // start in sketch file
29082909
for (String line : expanded.split("\n", -1)) {
29092910
String t = line.strip();
2910-
if (t.startsWith("# ") && t.length() > 2 && Character.isDigit(t.charAt(2))) continue;
2911+
// GNU line marker: "# N \"filename\" flags"
2912+
if (t.startsWith("# ") && t.length() > 2 && Character.isDigit(t.charAt(2))) {
2913+
// Check if this marker is for our scratch file
2914+
inScratchFile = t.contains(scratchPath) || t.contains("<command-line>") || t.contains("<built-in>");
2915+
// Also allow returning to scratch file from included user code
2916+
// but exclude system headers (/usr/include, /usr/lib, etc.)
2917+
if (t.contains("/usr/") || t.contains("/lib/") || t.contains("c++/")) inScratchFile = false;
2918+
continue; // never emit line markers themselves
2919+
}
29112920
if (t.startsWith("#line")) continue;
2921+
if (t.startsWith("#pragma")) continue; // strip pragmas from system headers
2922+
if (!inScratchFile) continue; // skip system header content
29122923
cleaned.append(line).append("\n");
29132924
}
29142925
return cleaned.toString();

src/java/Parser.java

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,26 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
277277
}
278278

279279
List<String> templateParams = List.of();
280+
// extern "C" / extern "C++" linkage specification -- consume verbatim
281+
if (checkKeyword("extern") && pos + 1 < tokens.size()
282+
&& tokens.get(pos + 1).type() == CppLexerTokenType.STRING_LITERAL) {
283+
advance(); advance(); // consume extern "C++"
284+
if (checkPunct("{")) {
285+
// extern "C++" { ... } block -- parse contents as top-level items
286+
advance(); // consume {
287+
List<TopLevelItem> result = new ArrayList<>();
288+
while (!isAtEnd() && !checkPunct("}")) {
289+
List<CppLexerToken> innerComments = consumeLeadingComments();
290+
if (checkPunct("}")) break;
291+
result.addAll(parseTopLevelItem(innerComments));
292+
}
293+
matchPunct("}");
294+
return result;
295+
} else {
296+
// extern "C++" single-declaration
297+
return parseTopLevelItem(leadingComments);
298+
}
299+
}
280300
if (matchKeyword("template")) {
281301
// Explicit template instantiation: "template class Foo<int>;" (no < after template)
282302
if (!checkOp("<")) {
@@ -684,22 +704,33 @@ private TypeDef parseTypeDef(List<CppLexerToken> leadingComments, List<String> t
684704
CppLexerToken start = peek();
685705
String kind = checkKeyword("class") ? "class" : "struct";
686706
advance();
707+
// alignas specifier: "struct alignas(64) CacheLine"
708+
if (checkKeyword("alignas") && pos + 1 < tokens.size() && tokens.get(pos + 1).isPunct("(")) {
709+
advance(); advance(); int _ad=1;
710+
while (!isAtEnd() && _ad > 0) { if (checkPunct("(")) _ad++; else if (checkPunct(")")) _ad--; advance(); }
711+
}
712+
// Also consume [[attributes]] before the name
713+
consumeAttributes();
687714
String name = expectIdentifier().text();
688715

689716
// Partial or explicit specialization: "template<typename T> struct Foo<T*>"
690717
// or "template<> struct TypeName<int>" -- capture the specialization arg
691718
// text and fold it into the name so CodeGen emits "TypeName<int>" correctly.
692-
if (checkOp("<") && looksLikeTemplateArgList()) {
719+
if (checkOp("<")) {
720+
// Specialization args: "Foo<T, N, false>" -- scan to matching > manually
693721
int argStart = pos;
694-
try { parseTemplateArgList(); } catch (ParseException ignored) {}
722+
advance(); // consume <
723+
int depth = 1;
724+
while (!isAtEnd() && depth > 0) {
725+
if (checkOp("<")) { depth++; advance(); }
726+
else if (checkOp(">")) { depth--; if (depth > 0) advance(); else advance(); }
727+
else if (checkOp(">>")) { depth -= 2; splitTrailingShiftIntoTwoCloseAngles(); if (depth > 0) advance(); else advance(); }
728+
else advance();
729+
}
695730
int argEnd = pos;
696731
// Reconstruct the specialization suffix verbatim from tokens
697-
StringBuilder spec = new StringBuilder("<");
698-
for (int i = argStart + 1; i < argEnd - 1; i++) {
699-
if (i > argStart + 1) spec.append(tokens.get(i-1).text().matches("[<>:,(]") ? "" : " ");
700-
spec.append(tokens.get(i).text());
701-
}
702-
spec.append(">");
732+
StringBuilder spec = new StringBuilder();
733+
for (int i = argStart; i < argEnd; i++) spec.append(tokens.get(i).text());
703734
name = name + spec.toString();
704735
}
705736

@@ -849,6 +880,34 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
849880
// Constructor dispatch: identifier matching the enclosing class name,
850881
// directly followed by '(' with no return type preceding it.
851882
// Also handle "constexpr ClassName(...)" -- consume constexpr first.
883+
// friend declaration: "friend class Foo;" or "friend Box<U> makeBox(U v);"
884+
if (checkKeyword("friend")) {
885+
int startPos = pos; advance(); // consume friend
886+
// Consume to ; skipping over <> and ()
887+
int _fd = 0;
888+
while (!isAtEnd()) {
889+
if (checkPunct("(") || checkOp("<")) { _fd++; advance(); }
890+
else if (checkPunct(")") || checkOp(">")) { _fd--; advance(); }
891+
else if (checkOp(">>")) { _fd -= 2; advance(); }
892+
else if (checkPunct(";") && _fd == 0) { advance(); break; }
893+
else if (checkPunct("{") && _fd == 0) {
894+
// friend function with body
895+
int bd = 1; advance();
896+
while (!isAtEnd() && bd > 0) { if (checkPunct("{")) bd++; else if (checkPunct("}")) bd--; advance(); }
897+
break;
898+
}
899+
else advance();
900+
}
901+
return List.of(new PreprocessorLine("// friend", tokens.get(startPos).line(), tokens.get(startPos).col(), leadingComments));
902+
}
903+
// explicit(...): conditional explicit specifier (C++20)
904+
if (checkKeyword("explicit") && pos + 1 < tokens.size() && tokens.get(pos + 1).isPunct("(")) {
905+
advance(); // consume explicit
906+
advance(); int _ed=1; // consume (
907+
while (!isAtEnd() && _ed > 0) { if (checkPunct("(")) _ed++; else if (checkPunct(")")) _ed--; advance(); }
908+
} else {
909+
matchKeyword("explicit"); // plain explicit
910+
}
852911
boolean isConstexprCtor = false;
853912
if (checkKeyword("constexpr") && pos + 1 < tokens.size()
854913
&& tokens.get(pos + 1).type() == CppLexerTokenType.IDENTIFIER
@@ -857,7 +916,9 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
857916
advance(); // consume constexpr
858917
isConstexprCtor = true;
859918
}
860-
if (check(CppLexerTokenType.IDENTIFIER) && peek().text().equals(enclosingClassName) && tokens.get(pos + 1).isPunct("(")) {
919+
// Constructor: match enclosingClassName OR just its base name (for specializations like Grid<T,N,false>)
920+
String enclosingBase = enclosingClassName.contains("<") ? enclosingClassName.substring(0, enclosingClassName.indexOf("<")) : enclosingClassName;
921+
if (check(CppLexerTokenType.IDENTIFIER) && (peek().text().equals(enclosingClassName) || peek().text().equals(enclosingBase)) && tokens.get(pos + 1).isPunct("(")) {
861922
return List.of(parseFunctionOrConstructorOrDestructor(leadingComments, templateParams));
862923
}
863924
return parseFunctionOrVariable(leadingComments, templateParams, false);
@@ -1658,14 +1719,17 @@ private boolean isIntegerTypeModifierOrBase(String s) {
16581719
*/
16591720
private void parseTemplateDefaultValue() {
16601721
int depth = 0;
1722+
int braceDepth = 0; // track {} so < inside lambdas doesn't affect angle depth
16611723
while (!isAtEnd()) {
1724+
if (checkPunct("{")) { braceDepth++; advance(); continue; }
1725+
if (checkPunct("}")) { if (braceDepth > 0) { braceDepth--; advance(); continue; } break; }
16621726
if (checkPunct("(") || checkPunct("[")) { depth++; advance(); }
16631727
else if (checkPunct(")") || checkPunct("]")) {
16641728
if (depth == 0) break;
16651729
depth--; advance();
16661730
}
1667-
else if (checkOp("<")) { depth++; advance(); }
1668-
else if (checkOp(">")) {
1731+
else if (checkOp("<") && braceDepth == 0) { depth++; advance(); }
1732+
else if (checkOp(">") && braceDepth == 0) {
16691733
if (depth == 0) break;
16701734
depth--; advance();
16711735
}
@@ -2956,10 +3020,19 @@ private Block parseBlock() {
29563020
private List<Statement> parseStatementOrMultiDecl(List<CppLexerToken> leadingComments) {
29573021
if (isStructuredBindingStart()) return List.of(parseStructuredBinding(leadingComments));
29583022
// Local struct/class definition: "struct Point { float x, y; }; Point p{...}"
3023+
// Also handle forward decl: "struct LocalFwd;" -- consume and skip
29593024
if ((checkKeyword("struct") || checkKeyword("class"))
29603025
&& pos + 1 < tokens.size()
29613026
&& (tokens.get(pos + 1).type() == CppLexerTokenType.IDENTIFIER
29623027
|| tokens.get(pos + 1).isPunct("{"))) {
3028+
// Forward declaration: "struct Foo;" -- just consume and emit empty
3029+
if (tokens.get(pos + 1).type() == CppLexerTokenType.IDENTIFIER
3030+
&& pos + 2 < tokens.size() && tokens.get(pos + 2).isPunct(";")) {
3031+
CppLexerToken sk = peek(); advance(); advance(); advance(); // struct Name ;
3032+
return List.of(new ExprStatement(
3033+
new Identifier("", sk.line(), sk.col(), List.of()),
3034+
sk.line(), sk.col(), leadingComments));
3035+
}
29633036
TypeDef td = parseTypeDef(leadingComments, List.of());
29643037
matchPunct(";");
29653038
// Emit struct as verbatim text via CodeGen

0 commit comments

Comments
 (0)