1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package optkl.codebuilders;
26
27 import jdk.incubator.code.Block;
28 import jdk.incubator.code.Op;
29 import jdk.incubator.code.TypeElement;
30 import jdk.incubator.code.dialect.core.CoreOp;
31 import jdk.incubator.code.dialect.java.ArrayType;
32 import jdk.incubator.code.dialect.java.JavaOp;
33 import jdk.incubator.code.dialect.java.JavaType;
34 import optkl.FuncOpParams;
35 import optkl.OpHelper;
36 import optkl.ParamVar;
37 import optkl.util.Mutable;
38 import optkl.util.ops.Precedence;
39
40 import java.util.function.Consumer;
41
42 import static optkl.OpHelper.FieldAccess.fieldAccess;
43 import static optkl.OpHelper.Invoke.invoke;
44 import static optkl.OpHelper.Ternary.ternary;
45
46 public abstract class JavaOrC99StyleCodeBuilder<T extends JavaOrC99StyleCodeBuilder<T,SCBC>,SCBC extends ScopedCodeBuilderContext>
47 extends ScopedCodeBuilder<T,SCBC>
48 implements BabylonOpDispatcher<T,SCBC>{
49
50 ScopedCodeBuilderContext scopedCodeBuilderContext(){
51 throw new RuntimeException("we are fake. This should be removed ");
52 }
53
54 public final T assign(Consumer<T> lhs, Consumer<T> rhs){
55 lhs.accept(self());
56 space().equals().space();
57 rhs.accept(self());
58 return self();
59 }
60
61 public final T cast(Consumer<T> type){
62 return paren(_-> type.accept(self()));
63 }
64
65 public final T returnKeyword(Consumer<T> exp){
66 return returnKeyword().space().paren(_-> exp.accept(self())).semicolon();
67 }
68 public final T forLoop(Consumer<T> init, Consumer<T> test, Consumer<T>mutate, Consumer<T>body) {
69 return forKeyword()
70 .paren(_->{
71 init.accept(self());
72 semicolon().space();
73 test.accept(self());
74 semicolon().space();mutate.accept(self());
75 })
76 .braceNlIndented(body::accept);
77 }
78
79 public final T literal(TypeElement typeElement, String string){
80 if (typeElement.toString().equals("java.lang.String")){
81 dquote().escaped(string).dquote();
82 }else{
83 literal(string);
84 }
85 return self();
86 }
87
88
89 @Override
90 public T type( JavaType javaType) {
91 return typeName(javaType.toString());
92 }
93
94
95 @Override
96 public T varLoadOp( CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
97 Op resolve = scopedCodeBuilderContext().resolve(varLoadOp.operands().getFirst());
98 if (resolve instanceof CoreOp.VarOp varOp) {
99 varName(varOp);
100 }else if (resolve instanceof CoreOp.VarAccessOp.VarLoadOp){
101 varName(varLoadOp);
102 }
103 return self();
104 }
105
106 @Override
107 public T varStoreOp( CoreOp.VarAccessOp.VarStoreOp varStoreOp) {
108 Op op = scopedCodeBuilderContext().resolve(varStoreOp.operands().getFirst());
109 varName((CoreOp.VarOp) op);
110 equals().parenthesisIfNeeded( varStoreOp, ((Op.Result)varStoreOp.operands().get(1)).op());
111 return self();
112 }
113
114
115 @Override
116 public final T varOp( CoreOp.VarOp varOp) {
117 if (varOp.isUninitialized()) {
118 type( (JavaType) varOp.varValueType()).space().varName(varOp);
119 } else {
120 if (scopedCodeBuilderContext().isVarOpFinal(varOp)) {
121 constKeyword().space();
122 }
123 type( (JavaType) varOp.varValueType()).space().varName(varOp).space().equals().space();
124 var first = varOp.operands().getFirst();
125 if (first instanceof Op.Result result) {
126 parenthesisIfNeeded( varOp, result.op());
127 }else if (first instanceof Block.Parameter parameter) {
128 var p1 = parameter.declaringBlock().parameters().getFirst();
129
130 var r = parameter.uses().iterator().next();
131 //parenthesisIfNeeded( varOp, r.op());
132 // if (r.op() instanceof CoreOp.VarOp varOp1){
133 // identifier(varOp1.varName());
134 // }
135 blockInlineComment("param "+r);
136 }else{
137 blockInlineComment("look at varOp "+first);
138 }
139 }
140 return self();
141 }
142
143 @Override
144 public final T varOp( CoreOp.VarOp varOp, ParamVar paramVar) {
145 varName(varOp);
146 return self();
147 }
148
149 @Override
150 public T fieldLoadOp( JavaOp.FieldAccessOp.FieldLoadOp fieldLoadOp) {
151 var fieldAccess = fieldAccess(scopedCodeBuilderContext().lookup(),fieldLoadOp);
152 if (fieldAccess.operandCount()==0 && fieldAccess.isPrimitive() ) {
153 literal(fieldAccess.getStaticFinalPrimitiveValue().toString());
154 } else {
155 identifier(fieldAccess.name());
156 }
157 return self();
158 }
159
160 @Override
161 public final T fieldStoreOp( JavaOp.FieldAccessOp.FieldStoreOp fieldStoreOp) {
162 var fieldAccess = fieldAccess(scopedCodeBuilderContext().lookup(),fieldStoreOp);
163 identifier(fieldAccess.name()).space().equals().space();
164 recurse(((Op.Result)fieldAccess.op().operands().get(0)).op());
165 dot();
166 recurse(((Op.Result)fieldAccess.op().operands().get(1)).op());
167 return self();
168 }
169
170
171 @Override
172 public final T unaryOp( JavaOp.UnaryOp unaryOp) {
173 symbol(unaryOp).parenthesisIfNeeded( unaryOp, ((Op.Result)unaryOp.operands().getFirst()).op());
174 return self();
175 }
176
177 @Override
178 public final T binaryOp( JavaOp.BinaryOp binaryOp) {
179 parenthesisIfNeeded( binaryOp, OpHelper.lhsResult(binaryOp).op());
180 symbol(binaryOp);
181 parenthesisIfNeeded( binaryOp, OpHelper.rhsResult(binaryOp).op());
182 return self();
183 }
184
185
186 @Override
187 public final T conditionalOp( JavaOp.JavaConditionalOp logicalOp) {
188 OpHelper.lhsOps(logicalOp).stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
189 space().symbol(logicalOp).space();
190 OpHelper.rhsOps(logicalOp).stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o-> recurse( o));
191 return self();
192 }
193
194 @Override
195 public final T binaryTestOp( JavaOp.BinaryTestOp binaryTestOp) {
196 parenthesisIfNeeded( binaryTestOp, OpHelper.lhsResult(binaryTestOp).op());
197 symbol(binaryTestOp);
198 parenthesisIfNeeded( binaryTestOp, OpHelper.rhsResult(binaryTestOp).op());
199 return self();
200 }
201
202 @Override
203 public T convOp( JavaOp.ConvOp convOp) {
204 // TODO: I think we need to work out how to handle doubles. If I remove this OpenCL on MAC complains (no FP64)
205 if (convOp.resultType() == JavaType.DOUBLE) {
206 paren(_ -> type(JavaType.FLOAT)); // why double to float?
207 } else {
208 paren(_ -> type((JavaType)convOp.resultType()));
209 }
210 parenthesisIfNeeded( convOp, ((Op.Result) convOp.operands().getFirst()).op());
211 return self();
212 }
213
214 @Override
215 public final T constantOp( CoreOp.ConstantOp constantOp) {
216 if (constantOp.value() == null) {
217 nullConst();
218 } else {
219 literal(constantOp.resultType(),constantOp.value().toString());
220 }
221 return self();
222 }
223
224 @Override
225 public final T yieldOp( CoreOp.YieldOp yieldOp) {
226 if (yieldOp.operands().getFirst() instanceof Op.Result result) {
227 recurse( result.op());
228 }
229 return self();
230 }
231
232
233
234 @Override
235 public final T tupleOp( CoreOp.TupleOp tupleOp) {
236 commaSpaceSeparated(tupleOp.operands(),operand->{
237 if (operand instanceof Op.Result result) {
238 recurse( result.op());
239 } else {
240 throw new IllegalStateException("handle tuple");
241 //comment("/* nothing to tuple */");
242 }
243 });
244 return self();
245 }
246
247 @Override
248 public final T funcCallOp( CoreOp.FuncCallOp funcCallOp) {
249 funcName(funcCallOp);
250 paren(_ ->
251 commaSpaceSeparated(
252 funcCallOp.operands().stream().filter(e->e instanceof Op.Result ).map(e->(Op.Result)e),
253 result -> recurse(result.op())
254 )
255 );
256 return self();
257 }
258
259 @Override
260 public final T labeledOp( JavaOp.LabeledOp labeledOp) {
261 var labelNameOp = labeledOp.bodies().getFirst().entryBlock().ops().getFirst();
262 CoreOp.ConstantOp constantOp = (CoreOp.ConstantOp) labelNameOp;
263 literal(constantOp.value().toString()).colon().nl();
264 var forLoopOp = labeledOp.bodies().getFirst().entryBlock().ops().get(1);
265 recurse(forLoopOp);
266 return self();
267 }
268
269 @Override
270 public final T breakOp( JavaOp.BreakOp breakOp) {
271 breakKeyword();
272 if (!breakOp.operands().isEmpty() && breakOp.operands().getFirst() instanceof Op.Result result) {
273 space();
274 if (result.op() instanceof CoreOp.ConstantOp c) {
275 literal(c.value().toString());
276 }
277 }
278 return self();
279 }
280
281 @Override
282 public final T continueOp( JavaOp.ContinueOp continueOp) {
283 if (!continueOp.operands().isEmpty()
284 && continueOp.operands().getFirst() instanceof Op.Result result
285 && result.op() instanceof CoreOp.ConstantOp c
286 ) {
287 continueKeyword().space().literal(c.value().toString());
288 } else if (scopedCodeBuilderContext().isInFor()) {
289 // nope
290 } else {
291 continueKeyword();
292 }
293
294 return self();
295 }
296
297 @Override
298 public final T ifOp( JavaOp.IfOp ifOp) {
299 scopedCodeBuilderContext().ifScope(ifOp, () -> {
300 var lastWasBody = Mutable.of(false);
301 var i = Mutable.of(0);
302 // We probably should just use a regular for loop here ;)
303 ifOp.bodies().forEach(b->{
304 int idx = i.get();
305 if (b.yieldType() instanceof JavaType javaType && javaType == JavaType.VOID) {
306 if (ifOp.bodies().size() > idx && ifOp.bodies().get(idx).entryBlock().ops().size() > 1){
307 if (lastWasBody.get()) {
308 elseKeyword();
309 }
310 braceNlIndented(_ ->
311 nlSeparated(OpHelper.Statement.statements(ifOp.bodies().get(idx).entryBlock()),
312 root-> statement(root)
313 ));
314 }
315 lastWasBody.set(true);
316 } else {
317 when(idx>0,_-> elseKeyword().space());
318 ifKeyword().paren(_ ->
319 ifOp.bodies().get(idx).entryBlock() // get the entryblock if bodies[c.value]
320 .ops().stream().filter(o->o instanceof CoreOp.YieldOp) // we want all the yields
321 .forEach((yield) -> recurse( yield))
322 );
323 lastWasBody.set(false);
324 }
325 i.set(i.get()+1);
326 });
327 });
328 return self();
329 }
330
331 @Override
332 public final T whileOp( JavaOp.WhileOp whileOp) {
333 whileKeyword().paren(_ ->
334 OpHelper.entryBlockOfBodyN(whileOp, 0)
335 .ops().stream().filter(o -> o instanceof CoreOp.YieldOp)
336 .forEach(o -> recurse( o))
337 );
338 braceNlIndented(_ ->
339 nlSeparated(OpHelper.Statement.bodyStatements(whileOp.loopBody()),
340 statement->statement(statement)
341 )
342 );
343 return self();
344 }
345
346 @Override
347 public final T forOp( JavaOp.ForOp forOp) {
348 scopedCodeBuilderContext().forScope(forOp, () ->
349 forKeyword().paren(_ -> {
350 forOp.init().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
351 semicolon().space();
352 forOp.cond().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
353 semicolon().space();
354 commaSpaceSeparated(
355 OpHelper.Statement.statements(forOp.update().entryBlock()),
356 op -> recurse( op)
357 );
358 }).braceNlIndented(_ ->
359 nlSeparated(OpHelper.Statement.bodyStatements(forOp.loopBody()),
360 statement ->statement(statement)
361 )
362 )
363 );
364 return self();
365 }
366
367 @Override
368 public T invokeOp( JavaOp.InvokeOp invokeOp) {
369 var invoke = invoke(scopedCodeBuilderContext().lookup(),invokeOp);
370
371 funcName(invoke.op()).paren(_ ->
372 commaSpaceSeparated(invoke.op().operands(),
373 op -> {if (op instanceof Op.Result result) {recurse( result.op());}
374 })
375 );
376
377 return self();
378 }
379
380 @Override
381 public final T conditionalExpressionOp( JavaOp.ConditionalExpressionOp ternaryOp) {
382 OpHelper.Ternary ternary = ternary(scopedCodeBuilderContext().lookup(),ternaryOp);
383 ternary.condBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
384 questionMark();
385 ternary.thenBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
386 colon();
387 ternary.elseBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
388 return self();
389 }
390
391 /**
392 * Wrap paren() of precedence of op is higher than parent.
393 *
394
395 * @param parent
396 * @param child
397 */
398 @Override
399 public final T parenthesisIfNeeded( Op parent, Op child) {
400 return parenWhen(Precedence.needsParenthesis(parent,child), _ -> recurse( child));
401 }
402
403 @Override
404 public final T returnOp( CoreOp.ReturnOp returnOp) {
405 returnKeyword().when(!returnOp.operands().isEmpty(),
406 $-> $.space().parenthesisIfNeeded( returnOp, ((Op.Result) returnOp.operands().getFirst()).op())
407 );
408 return self();
409 }
410
411 public final T statement(Op op) {
412 recurse( op);
413 if (switch (op){
414 case JavaOp.ForOp _ -> false;
415 case JavaOp.WhileOp _ -> false;
416 case JavaOp.IfOp _ -> false;
417 case JavaOp.LabeledOp _ -> false;
418 case JavaOp.YieldOp _ -> false;
419 case CoreOp.TupleOp _ ->false;
420 default -> true;
421 }
422 ){
423 semicolon();
424 }
425 return self();
426 }
427
428 public final T declareParam( FuncOpParams.Info param){
429 return type((JavaType) param.parameter.type()).space().varName(param.varOp);
430 }
431
432 @Override
433 public T newOp( JavaOp.NewOp newOp) {
434 newKeyword().space().type((JavaType) newOp.type());
435 if (newOp.operands().isEmpty()){
436 ocparen();
437 }else {
438 if (newOp.type() instanceof ArrayType){
439 brace(_ -> {
440 commaSpaceSeparated(newOp.operands(),
441 op -> {
442 if (op instanceof Op.Result result) {
443 recurse( result.op());
444 }
445 });
446 });
447 }else {
448 paren(_ -> {
449 commaSpaceSeparated(newOp.operands(),
450 op -> {
451 if (op instanceof Op.Result result) {
452 recurse( result.op());
453 }
454 });
455 });
456 }
457 }
458 return self();
459 }
460 @Override
461 public T arrayLoadOp( JavaOp.ArrayAccessOp.ArrayLoadOp arrayLoadOp){
462 recurse(((Op.Result)arrayLoadOp.operands().get(0)).op());
463 sbrace(_-> recurse(((Op.Result)arrayLoadOp.operands().get(1)).op()));
464 return self();
465 }
466
467 @Override
468 public T arrayStoreOp( JavaOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp){
469 recurse(((Op.Result)arrayStoreOp.operands().get(0)).op());
470 sbrace(_-> recurse(((Op.Result)arrayStoreOp.operands().get(1)).op()));
471 space().equals().space();
472 recurse(((Op.Result)arrayStoreOp.operands().get(2)).op());
473 return self();
474 }
475
476 @Override
477 public T enhancedForOp(JavaOp.EnhancedForOp enhancedForOp){
478 forKeyword().paren(_-> {
479 enhancedForOp.initialization().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
480 space().colon().space().blockInlineComment("Get rid of = before this");
481 enhancedForOp.expression().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
482 }).braceNlIndented(_->
483 nlSeparated(OpHelper.Statement.bodyStatements(enhancedForOp.loopBody()),
484 this::statement
485 )
486
487 );
488 return self();
489 }
490
491 @Override
492 public T blockOp( JavaOp.BlockOp blockOp) {
493 return braceNlIndented(_-> nlSeparated(OpHelper.Statement.statements(blockOp.body().entryBlock()), this::statement));
494 }
495
496 @Override
497 public T concatOp( JavaOp.ConcatOp concatOp) {
498 return
499 recurse( ((Op.Result)concatOp.operands().get(0)).op()).
500 add().recurse( ((Op.Result)concatOp.operands().get(1)).op());
501 }
502
503 @Override
504 public final T lambdaOp( JavaOp.LambdaOp lambdaOp) {
505 scopedCodeBuilderContext().lambdaScope(lambdaOp,()-> {
506 var parameters = lambdaOp.body().entryBlock().parameters();
507 // if (parameters.isEmpty()) {
508 // ocparen();
509 //}else{
510 parenWhen(parameters.size()!=1,_->{
511 commaSpaceSeparated(parameters,$->
512 varName((CoreOp.VarOp)$.uses().stream().findFirst().get().op())
513 );
514 });
515 //}
516 space().rarrow().space();
517 braceNlIndented(_ -> {
518 nlSeparated(OpHelper.Statement.bodyStatements(lambdaOp.body()),
519 op->{
520 if (op instanceof CoreOp.VarOp varOp
521 && varOp.operands().getFirst() instanceof Block.Parameter parameter
522 ){
523 varName(varOp);
524 }else {
525 statement(op);
526 }
527 }
528 );
529 });
530 }
531 );
532 return self();
533 }
534 }