-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCppCLI.java
More file actions
194 lines (170 loc) · 8.51 KB
/
Copy pathCppCLI.java
File metadata and controls
194 lines (170 loc) · 8.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package processing.mode.cpp;
import java.io.*;
import java.util.*;
import java.nio.charset.StandardCharsets;
/**
* Standalone CLI translator for the web compile server.
* Reads PDE/Java-style sketch from stdin, outputs C++ to stdout.
*/
public class CppCLI {
// Single source of truth for lifecycle method names -- defined in CppBuild.
private static final Set<String> LIFECYCLE = CppBuild.LIFECYCLE_METHOD_NAMES;
public static void main(String[] args) throws Exception {
String input = new String(System.in.readAllBytes(), StandardCharsets.UTF_8);
try {
System.out.print(translate(input));
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
private static String translate(String input) throws Exception {
// Delegate all pre-AST transforms to prepareCode() -- single source of truth.
CppBuild.PreparedCode prepared = CppBuild.prepareCode(input);
String code = prepared.code;
boolean hasSetup = code.contains("void setup(");
boolean hasDraw = code.contains("void draw(");
// __has_include blocks go to file scope directly -- never into the parser.
StringBuilder hasIncludePreNs = new StringBuilder();
for (String block : prepared.hasIncludeBlocks.values()) {
hasIncludePreNs.append(block);
}
StringBuilder out = new StringBuilder();
StringBuilder preNs = new StringBuilder();
appendHeader(out);
if (hasSetup || hasDraw) {
String result = runPipeline(code, preNs);
System.out.print(hasIncludePreNs);
System.out.print(preNs);
System.out.print(out);
System.out.print(result);
if (!code.contains("int main")) {
System.out.print("\nint main() { Processing::_PSketch sketch; sketch.run(); return 0; }\n");
}
return "";
} else if (code.contains("int main")) {
return hasIncludePreNs.toString() + preNs.toString() + out.toString() + code;
} else {
out.append("\nnamespace Processing {\n\n");
out.append(code);
out.append("\n} // namespace Processing\n\n");
out.append("int main() { Processing::_PSketch sketch; sketch.run(); return 0; }\n");
return hasIncludePreNs.toString() + preNs.toString() + out.toString();
}
}
private static String runPipeline(String code, StringBuilder preNs) throws Exception {
CompilationUnit cu = Parser.parse(code);
CppBuild.AstPipelineResult pipe = CppBuild.runAstPipeline(cu);
StringBuilder sb = new StringBuilder();
sb.append("\nnamespace Processing {\n\n");
// Route preprocessor directives and namespace decls to file scope or namespace body.
List<TopLevelItem> effectiveRest = new ArrayList<>();
for (TopLevelItem item : pipe.filteredRest) {
if (item instanceof PreprocessorLine pl) {
if (CppBuild.isFileScopePreprocessorLine(pl)) preNs.append(CodeGen.generateNode(item, 0));
else sb.append(CodeGen.generateNode(item, 0));
continue;
}
if (item instanceof NamespaceDecl nd) {
if (nd.name().equals("std")) preNs.append(CodeGen.generateNode(nd, 0));
else sb.append(CodeGen.generateNode(nd, 0));
continue;
}
if (item instanceof UsingNamespaceDecl) {
sb.append(CodeGen.generateNode(item, 0)); continue;
}
effectiveRest.add(item);
}
// Emit in same order as writeSketchImpl.
for (TopLevelItem e : pipe.enumResult.enums) sb.append(CodeGen.generateNode(e, 0));
for (FunctionDecl fd : pipe.forwardDecls) sb.append(CodeGen.generateNode(fd, 0));
for (var v : pipe.arrayResult.hoistedSizingConstants) sb.append(CodeGen.generateNode(v, 0));
java.util.Set<String> autoHoistedNames = new java.util.HashSet<>();
for (TopLevelItem av : pipe.autoHoisted) if (av instanceof VariableDecl avd) autoHoistedNames.add(avd.name());
for (var v : pipe.depResult.hoistedVariables) {
if (!autoHoistedNames.contains(v.name())) sb.append(CodeGen.generateNode(v, 0));
}
for (TypeDef td : pipe.finalClasses) sb.append(CodeGen.generateNode(td, 0));
for (TopLevelItem av : pipe.autoHoisted) sb.append(CodeGen.generateNode(av, 0));
for (var v : pipe.arrayResult.hoistedArrays) sb.append(CodeGen.generateNode(v, 0));
for (FunctionDecl fd : pipe.depResult.hoistedFunctions) sb.append(CodeGen.generateNode(fd, 0));
// Collect lifecycle bodies; remaining items go into _PSketch.
List<FunctionDecl> lifecycleBodies = new ArrayList<>();
List<TopLevelItem> sketchMembers = new ArrayList<>();
for (TopLevelItem item : effectiveRest) {
if (item instanceof FunctionDecl fd && fd.body() != null && LIFECYCLE.contains(fd.name())) {
lifecycleBodies.add(fd);
} else {
sketchMembers.add(item);
}
}
// Strip redundant forward decls (same as writeSketchImpl).
java.util.Set<String> definedFns = new java.util.HashSet<>();
for (TopLevelItem item : sketchMembers)
if (item instanceof FunctionDecl fd && fd.body() != null)
definedFns.add(fd.name() + "/" + fd.params().size());
sketchMembers.removeIf(item -> item instanceof FunctionDecl fd
&& fd.body() == null && !fd.isPureVirtual()
&& definedFns.contains(fd.name() + "/" + fd.params().size()));
// Hoist TopLevelStatements to namespace scope.
List<TopLevelItem> nsHoisted = new ArrayList<>(), realMembers = new ArrayList<>();
for (TopLevelItem item : sketchMembers) {
if (item instanceof TopLevelStatement) nsHoisted.add(item);
else realMembers.add(item);
}
for (TopLevelItem item : nsHoisted) sb.append(CodeGen.generateNode(item, 0));
// Build _PSketch with lifecycle methods inlined.
sb.append("class _PSketch : public PApplet {\npublic:\n");
for (TopLevelItem item : realMembers) sb.append(CodeGen.generateNode(item, 1));
Map<String,FunctionDecl> bodyMap = new LinkedHashMap<>();
for (FunctionDecl fd : lifecycleBodies) bodyMap.put(fd.name(), fd);
for (String name : LIFECYCLE) {
FunctionDecl fd = bodyMap.get(name);
if (fd == null) continue;
String rendered = CodeGen.generateNode(fd, 0).strip();
if (!rendered.contains("override"))
rendered = rendered.replaceFirst("(\\b" + name + "\\s*\\([^)]*\\)\\s*)", "$1 override ");
for (String line : rendered.split("\n", -1))
sb.append(" ").append(line).append("\n");
}
sb.append("};\n");
sb.append("\n} // namespace Processing\n");
return sb.toString();
}
/**
* Produces the full C++ source string for a sketch, including #line directives,
* for use by CppLinter (g++ -fsyntax-only).
*/
public static String translateForLint(String sketchCode) throws Exception {
CppBuild.PreparedCode prepared = CppBuild.prepareCode(sketchCode);
String code = prepared.code;
boolean hasSetup = code.contains("void setup(");
boolean hasDraw = code.contains("void draw(");
StringBuilder hasIncludePreNs = new StringBuilder();
for (String block : prepared.hasIncludeBlocks.values())
hasIncludePreNs.append(block);
StringBuilder preNs = new StringBuilder();
StringBuilder result = new StringBuilder();
if (hasSetup || hasDraw) {
String body = runPipeline(code, preNs);
result.append(hasIncludePreNs);
result.append(preNs);
result.append("#include \"Processing.h\"\n");
result.append("using namespace std;\n");
result.append(body);
} else {
result.append(hasIncludePreNs);
result.append(preNs);
result.append("#include \"Processing.h\"\n");
result.append("using namespace std;\n");
result.append("\nnamespace Processing {\n\n");
result.append(code);
result.append("\n} // namespace Processing\n");
}
return result.toString();
}
private static void appendHeader(StringBuilder out) {
out.append("#include \"Processing.h\"\n");
out.append("using namespace std;\n");
}
}