11package processing .mode .cpp ;
22
33import java .io .*;
4- import java .lang .reflect .*;
54import java .util .*;
65import java .nio .charset .StandardCharsets ;
76
1110 */
1211public class CppCLI {
1312
14- private static final Set <String > LIFECYCLE = Set .of (
15- "setup" ,"draw" ,"mousePressed" ,"mouseReleased" ,"mouseClicked" ,
16- "mouseMoved" ,"mouseDragged" ,"mouseWheel" ,
17- "keyPressed" ,"keyReleased" ,"keyTyped" ,"settings"
18- );
13+ // Single source of truth for lifecycle method names -- defined in CppBuild.
14+ private static final Set <String > LIFECYCLE = CppBuild .LIFECYCLE_METHOD_NAMES ;
1915
2016 public static void main (String [] args ) throws Exception {
2117 String input = new String (System .in .readAllBytes (), StandardCharsets .UTF_8 );
@@ -28,95 +24,115 @@ public static void main(String[] args) throws Exception {
2824 }
2925
3026 private static String translate (String input ) throws Exception {
31- CppBuild b = allocate ();
32- String code = input ;
33- code = invoke (b , "sanitize" , code );
34- code = invoke (b , "removeUserIncludes" , code );
35- code = code .replaceAll ("(\\ bfinal_suspend\\ s*\\ ([^)]*\\ ))(\\ s*\\ {)" , "$1 noexcept$2" );
36- code = code .replaceAll ("(\\ binitial_suspend\\ s*\\ ([^)]*\\ ))(\\ s*\\ {)" , "$1 noexcept$2" );
37- code = code .replaceAll ("\\ btranslate\\ s*\\ (\\ s*0+\\ .?0*f?\\ s*,\\ s*0+\\ .?0*f?\\ s*\\ )\\ s*;" , "" );
38- code = code .replaceAll ("\\ btranslate\\ s*\\ (\\ s*0+\\ .?0*f?\\ s*,\\ s*0+\\ .?0*f?\\ s*,\\ s*0+\\ .?0*f?\\ s*\\ )\\ s*;" , "" );
39- code = invoke (b , "stripRawStringLiterals" , code );
40- code = code .replaceAll ("(?<=[0-9a-fA-FxXbB])'(?=[0-9a-fA-F])" , "" );
41- code = invoke (b , "javaToC" , code );
42- code = invoke (b , "stripNamespaceProcessing" , code );
43- code = invoke (b , "preprocessMacros" , code );
27+ // Delegate all pre-AST transforms to prepareCode() -- single source of truth.
28+ CppBuild .PreparedCode prepared = CppBuild .prepareCode (input );
29+ String code = prepared .code ;
4430
4531 boolean hasSetup = code .contains ("void setup(" );
4632 boolean hasDraw = code .contains ("void draw(" );
4733
34+ // __has_include blocks go to file scope directly -- never into the parser.
35+ StringBuilder hasIncludePreNs = new StringBuilder ();
36+ for (String block : prepared .hasIncludeBlocks .values ()) {
37+ hasIncludePreNs .append (block );
38+ }
39+
4840 StringBuilder out = new StringBuilder ();
4941 StringBuilder preNs = new StringBuilder ();
5042 appendHeader (out );
5143
5244 if (hasSetup || hasDraw ) {
5345 String result = runPipeline (code , preNs );
46+ System .out .print (hasIncludePreNs );
5447 System .out .print (preNs );
5548 System .out .print (out );
5649 System .out .print (result );
57- System .out .print ("\n int main() { Processing::_PSketch sketch; sketch.run(); return 0; }\n " );
50+ if (!code .contains ("int main" )) {
51+ System .out .print ("\n int main() { Processing::_PSketch sketch; sketch.run(); return 0; }\n " );
52+ }
5853 return "" ;
54+ } else if (code .contains ("int main" )) {
55+ return hasIncludePreNs .toString () + preNs .toString () + out .toString () + code ;
5956 } else {
6057 out .append ("\n namespace Processing {\n \n " );
6158 out .append (code );
6259 out .append ("\n } // namespace Processing\n \n " );
6360 out .append ("int main() { Processing::_PSketch sketch; sketch.run(); return 0; }\n " );
64- return out .toString ();
61+ return hasIncludePreNs . toString () + preNs . toString () + out .toString ();
6562 }
6663 }
6764
6865 private static String runPipeline (String code , StringBuilder preNs ) throws Exception {
6966 CompilationUnit cu = Parser .parse (code );
70- EnumScopeExtractor .Result enumResult = EnumScopeExtractor .extract (cu .items ());
71- List <TopLevelItem > afterLifecycle = LifecycleRewriter .rewrite (enumResult .rest , LIFECYCLE );
72- ClassHoister .Result classResult = ClassHoister .hoist (afterLifecycle );
73- List <PSketchInjector .Result > injected = PSketchInjector .injectAll (classResult .hoistedClasses );
74- List <TypeDef > finalClasses = injected .stream ().map (PSketchInjector .Result ::typeDef ).toList ();
75- ArrayHoister .Result arrayResult = ArrayHoister .hoist (classResult .rest );
76- DependencyHoister .Result depResult = DependencyHoister .hoist (arrayResult .rest , finalClasses , LIFECYCLE );
77- List <FunctionDecl > forwardDecls = ForwardDeclGenerator .generate (depResult .hoistedFunctions );
67+ CppBuild .AstPipelineResult pipe = CppBuild .runAstPipeline (cu );
7868
7969 StringBuilder sb = new StringBuilder ();
8070 sb .append ("\n namespace Processing {\n \n " );
8171
82- List <TopLevelItem > filteredRest = new ArrayList <>();
83- for (TopLevelItem item : depResult .rest ) {
72+ // Route preprocessor directives and namespace decls to file scope or namespace body.
73+ List <TopLevelItem > effectiveRest = new ArrayList <>();
74+ for (TopLevelItem item : pipe .filteredRest ) {
8475 if (item instanceof PreprocessorLine pl ) {
85- if (pl . rawText (). startsWith ( "#include" )) preNs .append (CodeGen .generateNode (item , 0 ));
76+ if (CppBuild . isFileScopePreprocessorLine ( pl )) preNs .append (CodeGen .generateNode (item , 0 ));
8677 else sb .append (CodeGen .generateNode (item , 0 ));
8778 continue ;
8879 }
89- if (item instanceof NamespaceDecl || item instanceof UsingNamespaceDecl ) {
80+ if (item instanceof NamespaceDecl nd ) {
81+ if (nd .name ().equals ("std" )) preNs .append (CodeGen .generateNode (nd , 0 ));
82+ else sb .append (CodeGen .generateNode (nd , 0 ));
83+ continue ;
84+ }
85+ if (item instanceof UsingNamespaceDecl ) {
9086 sb .append (CodeGen .generateNode (item , 0 )); continue ;
9187 }
92- filteredRest .add (item );
88+ effectiveRest .add (item );
9389 }
9490
95- for (FunctionDecl fd : forwardDecls ) sb .append (CodeGen .generateNode (fd , 0 ));
96- for (TopLevelItem e : enumResult .enums ) sb .append (CodeGen .generateNode (e , 0 ));
91+ // Emit in same order as writeSketchImpl.
92+ for (TopLevelItem e : pipe .enumResult .enums ) sb .append (CodeGen .generateNode (e , 0 ));
93+ for (FunctionDecl fd : pipe .forwardDecls ) sb .append (CodeGen .generateNode (fd , 0 ));
94+ for (var v : pipe .arrayResult .hoistedSizingConstants ) sb .append (CodeGen .generateNode (v , 0 ));
95+ java .util .Set <String > autoHoistedNames = new java .util .HashSet <>();
96+ for (TopLevelItem av : pipe .autoHoisted ) if (av instanceof VariableDecl avd ) autoHoistedNames .add (avd .name ());
97+ for (var v : pipe .depResult .hoistedVariables ) {
98+ if (!autoHoistedNames .contains (v .name ())) sb .append (CodeGen .generateNode (v , 0 ));
99+ }
100+ for (TypeDef td : pipe .finalClasses ) sb .append (CodeGen .generateNode (td , 0 ));
101+ for (TopLevelItem av : pipe .autoHoisted ) sb .append (CodeGen .generateNode (av , 0 ));
102+ for (var v : pipe .arrayResult .hoistedArrays ) sb .append (CodeGen .generateNode (v , 0 ));
103+ for (FunctionDecl fd : pipe .depResult .hoistedFunctions ) sb .append (CodeGen .generateNode (fd , 0 ));
97104
98- // Collect lifecycle bodies from hoisted functions
105+ // Collect lifecycle bodies; remaining items go into _PSketch.
99106 List <FunctionDecl > lifecycleBodies = new ArrayList <>();
100- for (TopLevelItem item : depResult .hoistedFunctions ) {
101- if (item instanceof FunctionDecl fd && LIFECYCLE .contains (fd .name ()) && fd .body () != null )
107+ List <TopLevelItem > sketchMembers = new ArrayList <>();
108+ for (TopLevelItem item : effectiveRest ) {
109+ if (item instanceof FunctionDecl fd && fd .body () != null && LIFECYCLE .contains (fd .name ())) {
102110 lifecycleBodies .add (fd );
103- else sb .append (CodeGen .generateNode (item , 0 ));
111+ } else {
112+ sketchMembers .add (item );
113+ }
104114 }
105- for (TopLevelItem item : depResult .hoistedVariables ) sb .append (CodeGen .generateNode (item , 0 ));
106- for (TopLevelItem item : arrayResult .hoistedArrays ) sb .append (CodeGen .generateNode (item , 0 ));
107- for (TypeDef td : finalClasses ) sb .append (CodeGen .generateNode (td , 0 ));
108-
109- // Also collect from filteredRest
110- List <TopLevelItem > nonLifecycle = new ArrayList <>();
111- for (TopLevelItem item : filteredRest ) {
112- if (item instanceof FunctionDecl fd && LIFECYCLE .contains (fd .name ()) && fd .body () != null )
113- lifecycleBodies .add (fd );
114- else nonLifecycle .add (item );
115+
116+ // Strip redundant forward decls (same as writeSketchImpl).
117+ java .util .Set <String > definedFns = new java .util .HashSet <>();
118+ for (TopLevelItem item : sketchMembers )
119+ if (item instanceof FunctionDecl fd && fd .body () != null )
120+ definedFns .add (fd .name () + "/" + fd .params ().size ());
121+ sketchMembers .removeIf (item -> item instanceof FunctionDecl fd
122+ && fd .body () == null && !fd .isPureVirtual ()
123+ && definedFns .contains (fd .name () + "/" + fd .params ().size ()));
124+
125+ // Hoist TopLevelStatements to namespace scope.
126+ List <TopLevelItem > nsHoisted = new ArrayList <>(), realMembers = new ArrayList <>();
127+ for (TopLevelItem item : sketchMembers ) {
128+ if (item instanceof TopLevelStatement ) nsHoisted .add (item );
129+ else realMembers .add (item );
115130 }
116- for (TopLevelItem item : nonLifecycle ) sb .append (CodeGen .generateNode (item , 0 ));
131+ for (TopLevelItem item : nsHoisted ) sb .append (CodeGen .generateNode (item , 0 ));
117132
118- // Build _PSketch with inline lifecycle bodies
133+ // Build _PSketch with lifecycle methods inlined.
119134 sb .append ("class _PSketch : public PApplet {\n public:\n " );
135+ for (TopLevelItem item : realMembers ) sb .append (CodeGen .generateNode (item , 1 ));
120136 Map <String ,FunctionDecl > bodyMap = new LinkedHashMap <>();
121137 for (FunctionDecl fd : lifecycleBodies ) bodyMap .put (fd .name (), fd );
122138 for (String name : LIFECYCLE ) {
@@ -133,30 +149,46 @@ private static String runPipeline(String code, StringBuilder preNs) throws Excep
133149 return sb .toString ();
134150 }
135151
136- private static void appendHeader (StringBuilder out ) {
137- out .append ("#include \" Processing.h\" \n " );
138- out .append ("using namespace std;\n " );
139- }
152+ /**
153+ * Produces the full C++ source string for a sketch, including #line directives,
154+ * for use by CppLinter (g++ -fsyntax-only).
155+ */
156+ public static String translateForLint (String sketchCode ) throws Exception {
157+ CppBuild .PreparedCode prepared = CppBuild .prepareCode (sketchCode );
158+ String code = prepared .code ;
159+ boolean hasSetup = code .contains ("void setup(" );
160+ boolean hasDraw = code .contains ("void draw(" );
140161
141- private static String invoke (CppBuild b , String method , String code ) throws Exception {
142- Method m = getMethod (CppBuild .class , method , String .class );
143- m .setAccessible (true );
144- return (String ) m .invoke (b , code );
145- }
162+ StringBuilder hasIncludePreNs = new StringBuilder ();
163+ for (String block : prepared .hasIncludeBlocks .values ())
164+ hasIncludePreNs .append (block );
146165
147- private static Method getMethod (Class <?> cls , String name , Class <?>... params ) throws NoSuchMethodException {
148- while (cls != null ) {
149- try { return cls .getDeclaredMethod (name , params ); }
150- catch (NoSuchMethodException e ) { cls = cls .getSuperclass (); }
166+ StringBuilder preNs = new StringBuilder ();
167+ StringBuilder result = new StringBuilder ();
168+
169+ if (hasSetup || hasDraw ) {
170+ String body = runPipeline (code , preNs );
171+ result .append (hasIncludePreNs );
172+ result .append (preNs );
173+ result .append ("#include \" Processing.h\" \n " );
174+ result .append ("using namespace std;\n " );
175+ result .append (body );
176+ } else {
177+ result .append (hasIncludePreNs );
178+ result .append (preNs );
179+ result .append ("#include \" Processing.h\" \n " );
180+ result .append ("using namespace std;\n " );
181+ result .append ("\n namespace Processing {\n \n " );
182+ result .append (code );
183+ result .append ("\n } // namespace Processing\n " );
151184 }
152- throw new NoSuchMethodException ( name );
185+ return result . toString ( );
153186 }
154187
155- private static CppBuild allocate () throws Exception {
156- Class <?> unsafeClass = Class .forName ("sun.misc.Unsafe" );
157- Field f = unsafeClass .getDeclaredField ("theUnsafe" );
158- f .setAccessible (true );
159- sun .misc .Unsafe unsafe = (sun .misc .Unsafe ) f .get (null );
160- return (CppBuild ) unsafe .allocateInstance (CppBuild .class );
188+ private static void appendHeader (StringBuilder out ) {
189+ out .append ("#include \" Processing.h\" \n " );
190+ out .append ("using namespace std;\n " );
161191 }
192+
193+
162194}
0 commit comments