Skip to content

Commit a16d6bb

Browse files
committed
Parser: pack expansion, typename, lambda captures, template args
- parseTemplateArgList: consume trailing ... (pack expansion Ts...) - parseTemplateArg: handle standalone ... tokens - parseInitializerElement: allow ... after expr in brace-init {args...} - parseCapture: pack expansion in lambda capture [args...] - parseTypeRefAfterConst: consume typename for dependent types - parseBaseClassEntry: preserve virtual keyword in output - PSketchInjector: skip injection for transitive _PSketch inheritors - ClassHoister: full definition position beats forward decl position - CppBuild: out-of-class static members hoisted to namespace scope - CppBuild: depResult.hoistedVariables emitted before class definitions - CppBuild: E0006 Java static call syntax detection - Processing.h: KEY_0-KEY_9, SPACE constants
1 parent 9c748db commit a16d6bb

3 files changed

Lines changed: 84 additions & 1 deletion

File tree

mode/CppMode.jar

1.17 KB
Binary file not shown.

src/java/CppBuild.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,7 @@ private File writeSketchImpl(RunnerListener listener) throws IOException {
10271027
checkForUnsupportedJavaArraySyntax(code, listener);
10281028
checkForArrayListGetValueCopy(code, listener);
10291029
checkForArrayListGetDotAccess(code, listener);
1030+
checkForJavaStaticCallSyntax(code, listener);
10301031
code = stripRawStringLiterals(code);
10311032
code = code.replaceAll("(?<=[0-9a-fA-FxXbB])'(?=[0-9a-fA-F])", "");
10321033
code = javaToC(code);
@@ -2356,6 +2357,73 @@ private void checkForArrayListGetDotAccess(String code, RunnerListener listener)
23562357
}
23572358
}
23582359

2360+
// [E0006] Java static method/field call syntax: ClassName.member must be ClassName::member in C++
2361+
//
2362+
// Generalized rule: any identifier that starts with an uppercase letter followed
2363+
// by a "." and then another identifier (with optional "(") is likely a Java
2364+
// static access pattern. This covers:
2365+
// Integer.parseInt(...) -> Integer::parseInt(...)
2366+
// Math.PI -> Math::PI (or just PI)
2367+
// MyClass.CONSTANT -> MyClass::CONSTANT
2368+
// Collections.sort(...) -> Collections::sort(...) or std::sort
2369+
//
2370+
// Excluded: known instance-access patterns where the variable name
2371+
// happens to start with uppercase (e.g. PImage, PFont, PVector variables).
2372+
private void checkForJavaStaticCallSyntax(String code, RunnerListener listener) {
2373+
List<CppLexerToken> tokens;
2374+
try { tokens = new CppLexer(code).tokenize(); } catch (Exception e) { return; }
2375+
// Types known to be used as instances (not static-only classes)
2376+
java.util.Set<String> instanceTypes = java.util.Set.of(
2377+
"PImage", "PFont", "PVector", "PShape", "PGraphics",
2378+
"ArrayList", "IntList", "FloatList", "StringList",
2379+
"String", "Array", "Table", "TableRow", "JSONObject", "JSONArray",
2380+
"PrintWriter", "BufferedReader", "XML"
2381+
);
2382+
// Definitely-static classes (always trigger E0006)
2383+
java.util.Set<String> staticClasses = java.util.Set.of(
2384+
"Integer", "Float", "Double", "Long", "Boolean", "Character",
2385+
"Byte", "Short", "Math", "Arrays", "Collections", "System",
2386+
"Thread", "Runtime", "Class", "Objects"
2387+
);
2388+
for (int i = 0; i + 2 < tokens.size(); i++) {
2389+
CppLexerToken t0 = tokens.get(i);
2390+
CppLexerToken t1 = tokens.get(i + 1);
2391+
CppLexerToken t2 = tokens.get(i + 2);
2392+
// Pattern: IDENTIFIER . IDENTIFIER where first IDENTIFIER starts uppercase
2393+
if (t0.type() != CppLexerTokenType.IDENTIFIER) continue;
2394+
if (!t1.isPunct(".")) continue;
2395+
if (t2.type() != CppLexerTokenType.IDENTIFIER) continue;
2396+
String firstName = t0.text();
2397+
if (firstName.isEmpty() || !Character.isUpperCase(firstName.charAt(0))) continue;
2398+
if (instanceTypes.contains(firstName)) continue; // known instance type
2399+
// Only fire for known static classes OR if preceded by nothing
2400+
// (i.e. not "obj.UpperMethod()" which is a method call)
2401+
// Check context: if previous token is ")", "]", or identifier, skip
2402+
boolean preceded = i > 0 && (
2403+
tokens.get(i-1).isPunct(")")
2404+
|| tokens.get(i-1).isPunct("]")
2405+
|| tokens.get(i-1).isPunct(">")
2406+
|| tokens.get(i-1).type() == CppLexerTokenType.IDENTIFIER);
2407+
if (preceded && !staticClasses.contains(firstName)) continue;
2408+
// Check that next token after t2 is "(" or nothing method-like
2409+
boolean isCall = i + 3 < tokens.size() && tokens.get(i + 3).isPunct("(");
2410+
boolean isField = !isCall; // field access like Math.PI
2411+
if (!staticClasses.contains(firstName) && !isCall) continue; // only fire on known statics for field access
2412+
int line = t0.line();
2413+
String url = getWebsiteBaseUrl() + "/error/E0006.html";
2414+
String access = t2.text() + (isCall ? "(...)" : "");
2415+
String msg =
2416+
"\n[E0006] Java static access syntax is not valid in C++.\n" +
2417+
" Line " + line + ": \"" + firstName + "." + access + "\"\n" +
2418+
" In C++, static members use :: not .:\n" +
2419+
" " + firstName + "::" + access + "\n" +
2420+
" Reference: " + url + "\n";
2421+
System.err.println(msg);
2422+
listener.statusError("E0006: use " + firstName + "::" + t2.text() + " not . -- see console");
2423+
throw new AlreadyReportedException("E0006: Java static access syntax -- see console.");
2424+
}
2425+
}
2426+
23592427
private void checkForUnsupportedJavaArraySyntax(String code, RunnerListener listener) {
23602428
try {
23612429
CppJavaArrayCheck.check(code);

src/java/Parser.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,8 @@ private TypeRef parseTypeRef(boolean leadingConst) {
14131413
}
14141414

14151415
private TypeRef parseTypeRefAfterConst(boolean isConst) {
1416+
// typename X::Y -- dependent type name, consume and continue
1417+
if (checkKeyword("typename")) advance();
14161418

14171419
// decltype(expr) -- C++11 computed type
14181420
if (checkKeyword("decltype")) {
@@ -1570,8 +1572,11 @@ private List<TypeRef> parseTemplateArgList() {
15701572
List<TypeRef> args = new ArrayList<>();
15711573
if (!checkOp(">")) {
15721574
args.add(parseTemplateArg());
1575+
matchPunct("..."); // trailing pack expansion: "Ts..."
15731576
while (matchPunct(",")) {
1577+
if (checkOp(">") || checkOp(">>")) break;
15741578
args.add(parseTemplateArg());
1579+
matchPunct("..."); // trailing pack expansion
15751580
}
15761581
}
15771582
// Note: ">>" closing two nested template lists at once (e.g.
@@ -1647,6 +1652,12 @@ private TypeRef parseTemplateArg() {
16471652
dt.append(")");
16481653
return new NamedType(dt.toString(), List.of(), 0, false, false);
16491654
}
1655+
// Pack expansion "..." trailing a type: "Ts..." in template args
1656+
// Also handles standalone "..." as a variadic marker
1657+
if (checkPunct("...")) {
1658+
advance();
1659+
return new NamedType("...", List.of(), 0, false, false);
1660+
}
16501661
// sizeof expression: sizeof(T) or sizeof...(Args)
16511662
if (checkKeyword("sizeof")) {
16521663
int save = pos; advance();
@@ -2451,7 +2462,9 @@ private Expr parseInitializerElement() {
24512462
if (checkPunct("{")) {
24522463
return parseInitializerList(); // anonymous nested brace-init
24532464
}
2454-
return parseExpr(); // expression (may itself end with {} for named brace-init via parsePrimary)
2465+
Expr e = parseExpr();
2466+
matchPunct("..."); // pack expansion in brace-init: "{args...}"
2467+
return e;
24552468
}
24562469

24572470
/**
@@ -2537,6 +2550,8 @@ private Capture parseCapture() {
25372550
// "this" is a keyword capture: [this] or [&this]
25382551
if (checkKeyword("this")) { advance(); return new Capture("this", byRef); }
25392552
String name = expectIdentifier().text();
2553+
// Pack expansion in capture: "[args...]"
2554+
if (checkPunct("...")) { advance(); name = name + "..."; }
25402555
// Init-capture: "z = z * 2" or "w = x + y" -- encode into name string
25412556
if (checkOp("=")) {
25422557
advance();

0 commit comments

Comments
 (0)