@@ -551,6 +551,16 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
551551 }
552552 }
553553
554+ // Bare statement at top level: control-flow keywords can only be statements,
555+ // never declarations. Route directly to statement parsing (static-mode sketches).
556+ if (checkKeyword ("for" ) || checkKeyword ("while" ) || checkKeyword ("do" ) ||
557+ checkKeyword ("if" ) || checkKeyword ("switch" ) || checkKeyword ("return" ) ||
558+ checkKeyword ("break" ) || checkKeyword ("continue" ) ||
559+ checkKeyword ("try" ) || checkKeyword ("throw" )) {
560+ CppLexerToken start = peek ();
561+ Statement stmt = parseStatement (leadingComments );
562+ return List .of (new TopLevelStatement (stmt , start .line (), start .col (), leadingComments ));
563+ }
554564 return parseFunctionOrVariable (leadingComments , templateParams , true );
555565 }
556566
@@ -866,11 +876,9 @@ else if (checkOp(">>")) {
866876 // "class HScrollbar { ... }" has no trailing ';' at all. Processing's
867877 // own preprocessing tolerates this; this parser does too rather than
868878 // hard-requiring strict C++ grammar here.
869- matchPunct (";" );
870-
871879 TypeDef typeDef = new TypeDef (kind , name , templateParams , baseClasses , members , start .line (), start .col (), leadingComments );
872880 // Trailing declarators: "class Foo { ... } *ptr;" or "} a, b, *c;"
873- // Only enter if next token is an identifier or pointer star, not a keyword .
881+ // Only enter if there is NO trailing semicolon yet .
874882 boolean nextIsDeclarator = checkOp ("*" ) || checkPunct ("*" )
875883 || peek ().type () == CppLexerTokenType .IDENTIFIER ;
876884 if (!matchPunct (";" ) && !isAtEnd () && !checkPunct ("}" ) && nextIsDeclarator ) {
@@ -1519,8 +1527,30 @@ private boolean looksLikeTopLevelDeclarationOrFunction() {
15191527 || checkKeyword ("operator" )) {
15201528 return true ;
15211529 }
1522- // Constructor: type name followed by ( -- "RGBA(...)" inside struct RGBA
1523- if (checkPunct ("(" )) return true ;
1530+ // If the parsed type has template args (e.g. ArrayList<Ball>),
1531+ // it is unambiguously a declaration — template types are never bare calls.
1532+ if (type instanceof NamedType nt2 && !nt2 .templateArgs ().isEmpty ()) return true ;
1533+ // Constructor or bare call: type name followed by (
1534+ // Disambiguate: scan ahead to find matching ) then check next token.
1535+ // If next is { or : it's a constructor/function definition.
1536+ // If next is ; or another statement-like token it's a bare call.
1537+ if (checkPunct ("(" )) {
1538+ int scan = pos + 1 ; int depth = 1 ;
1539+ while (scan < tokens .size () && depth > 0 ) {
1540+ if (tokens .get (scan ).isPunct ("(" )) depth ++;
1541+ else if (tokens .get (scan ).isPunct (")" )) depth --;
1542+ scan ++;
1543+ }
1544+ // scan is now after the closing )
1545+ if (scan < tokens .size ()) {
1546+ String after = tokens .get (scan ).text ();
1547+ // Function/constructor definition: followed by { or :
1548+ if (after .equals ("{" ) || after .equals (":" )) return true ;
1549+ // Bare call statement: followed by ; or next statement
1550+ return false ;
1551+ }
1552+ return true ;
1553+ }
15241554 return check (CppLexerTokenType .IDENTIFIER );
15251555 } finally {
15261556 pos = save ;
0 commit comments