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.CodeType;
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 T body(Consumer<T> consumer) {
55 return braceNlIndented(consumer);
56 }
57
58 public T tern(Consumer<T> test, Consumer<T> ifTrue, Consumer<T> ifFalse){
59 return paren(test).questionMark().paren(ifTrue).colon().paren(ifFalse);
60 }
61
62 public T stmnt(Consumer<T> consumer){
63 consumer.accept(self());
64 return semicolon();
65 }
66
67 public T func(
68 Consumer<T> type,
69 String funcName,
70 Consumer<T> args,
71 Consumer<T> body) {
72 type.accept(self());
73 return sp().id(funcName).paren(args).body(body).nl().nl();
74 }
75
76 public final T assign(Consumer<T> lhs, Consumer<T> rhs){
77 lhs.accept(self());
78 sp().equals().sp();
79 rhs.accept(self());
80 return self();
81 }
82
83 public final T cast(Consumer<T> type){
84 return paren(_-> type.accept(self()));
85 }
86
87 public final T returnKeyword(Consumer<T> exp){
88 // return returnKeyword().space().paren(_-> exp.accept(self())).semicolon(); // This looks wrong. it is very rare for us to have to add trailing semicolons
89 returnKeyword().sp();
90 exp.accept(self());
91 return semicolon();
92 }
93 public final T forLoop(Consumer<T> init, Consumer<T> test, Consumer<T>mutate, Consumer<T>body) {
94 return forKeyword()
95 .paren(_->{
96 init.accept(self());
97 semicolon().sp();
98 test.accept(self());
99 semicolon().sp();mutate.accept(self());
100 })
101 .braceNlIndented(body::accept);
102 }
103
104 public final T literal(CodeType codeType, String string){
105 if (codeType.toString().equals("java.lang.String")){
106 dquote().escaped(string).dquote();
107 }else{
108 literal(string);
109 }
110 return self();
111 }
112
113
114 @Override
115 public T type( JavaType javaType) {
116 return type(javaType.toString());
117 }
118
119
120 @Override
121 public T varLoadOp( CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
122 Op resolve = scopedCodeBuilderContext().resolve(varLoadOp.operands().getFirst());
123 if (resolve instanceof CoreOp.VarOp varOp) {
124 varName(varOp);
125 }else if (resolve instanceof CoreOp.VarAccessOp.VarLoadOp){
126 varName(varLoadOp);
127 }
128 return self();
129 }
130
131 @Override
132 public T varStoreOp( CoreOp.VarAccessOp.VarStoreOp varStoreOp) {
133 Op op = scopedCodeBuilderContext().resolve(varStoreOp.operands().getFirst());
134 varName((CoreOp.VarOp) op);
135 equals().parenthesisIfNeeded( varStoreOp, ((Op.Result)varStoreOp.operands().get(1)).op());
136 return self();
137 }
138
139 @Override
140 public T varOp(CoreOp.VarOp varOp) {
141 if (varOp.isUninitialized()) {
142 type((JavaType) varOp.varValueType()).sp().varName(varOp);
143 } else {
144 if (scopedCodeBuilderContext().isVarOpFinal(varOp)) {
145 constKeyword().sp();
146 }
147
148 var first = varOp.operands().getFirst();
149 if (first instanceof Op.Result result) {
150 parenthesisIfNeeded(varOp, result.op());
151 } else if (first instanceof Block.Parameter parameter) {
152 var p1 = parameter.declaringBlock().parameters().getFirst();
153
154 var r = parameter.uses().iterator().next();
155 //parenthesisIfNeeded( varOp, r.op());
156 // if (r.op() instanceof CoreOp.VarOp varOp1){
157 // identifier(varOp1.varName());
158 // }
159 blockInlineComment("param " + r);
160 } else {
161 blockInlineComment("look at varOp " + first);
162 }
163 }
164 return self();
165 }
166
167 @Override
168 public final T varOp( CoreOp.VarOp varOp, ParamVar paramVar) {
169 varName(varOp);
170 return self();
171 }
172
173 @Override
174 public T fieldLoadOp( JavaOp.FieldAccessOp.FieldLoadOp fieldLoadOp) {
175 var fieldAccess = fieldAccess(scopedCodeBuilderContext().lookup(),fieldLoadOp);
176 if (fieldAccess.operandCount()==0 && fieldAccess.isPrimitive() ) {
177 literal(fieldAccess.getStaticFinalPrimitiveValue().toString());
178 } else {
179 id(fieldAccess.name());
180 }
181 return self();
182 }
183
184 @Override
185 public final T fieldStoreOp( JavaOp.FieldAccessOp.FieldStoreOp fieldStoreOp) {
186 var fieldAccess = fieldAccess(scopedCodeBuilderContext().lookup(),fieldStoreOp);
187 id(fieldAccess.name()).sp().equals().sp();
188 recurse(((Op.Result)fieldAccess.op().operands().get(0)).op());
189 dot();
190 recurse(((Op.Result)fieldAccess.op().operands().get(1)).op());
191 return self();
192 }
193
194
195 @Override
196 public final T unaryOp( JavaOp.UnaryOp unaryOp) {
197 symbol(unaryOp).parenthesisIfNeeded( unaryOp, ((Op.Result)unaryOp.operands().getFirst()).op());
198 return self();
199 }
200
201 @Override
202 public final T binaryOp( JavaOp.BinaryOp binaryOp) {
203 parenthesisIfNeeded( binaryOp, OpHelper.lhsResult(binaryOp).op());
204 symbol(binaryOp);
205 parenthesisIfNeeded( binaryOp, OpHelper.rhsResult(binaryOp).op());
206 return self();
207 }
208
209
210 @Override
211 public final T conditionalOp( JavaOp.JavaConditionalOp logicalOp) {
212 OpHelper.lhsOps(logicalOp).stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
213 sp().symbol(logicalOp).sp();
214 OpHelper.rhsOps(logicalOp).stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o-> recurse( o));
215 return self();
216 }
217
218 @Override
219 public final T compareOp( JavaOp.CompareOp compareOp) {
220 parenthesisIfNeeded( compareOp, OpHelper.lhsResult(compareOp).op());
221 symbol(compareOp);
222 parenthesisIfNeeded( compareOp, OpHelper.rhsResult(compareOp).op());
223 return self();
224 }
225
226 @Override
227 public T convOp( JavaOp.ConvOp convOp) {
228 // TODO: I think we need to work out how to handle doubles. If I remove this OpenCL on MAC complains (no FP64)
229 if (convOp.resultType() == JavaType.DOUBLE) {
230 paren(_ -> type(JavaType.FLOAT)); // why double to float?
231 } else {
232 paren(_ -> type((JavaType)convOp.resultType()));
233 }
234 parenthesisIfNeeded( convOp, ((Op.Result) convOp.operands().getFirst()).op());
235 return self();
236 }
237
238 @Override
239 public final T constantOp( CoreOp.ConstantOp constantOp) {
240 if (constantOp.value() == null) {
241 nullConst();
242 } else {
243 literal(constantOp.resultType(),constantOp.value().toString());
244 }
245 return self();
246 }
247
248 @Override
249 public final T yieldOp( CoreOp.YieldOp yieldOp) {
250 if (yieldOp.operands().getFirst() instanceof Op.Result result) {
251 recurse( result.op());
252 }
253 return self();
254 }
255
256
257
258 @Override
259 public final T tupleOp( CoreOp.TupleOp tupleOp) {
260 commaSpaceSeparated(tupleOp.operands(),operand->{
261 if (operand instanceof Op.Result result) {
262 recurse( result.op());
263 } else {
264 throw new IllegalStateException("handle tuple");
265 //comment("/* nothing to tuple */");
266 }
267 });
268 return self();
269 }
270
271 @Override
272 public final T funcCallOp( CoreOp.FuncCallOp funcCallOp) {
273 funcName(funcCallOp);
274 paren(_ ->
275 commaSpaceSeparated(
276 funcCallOp.operands().stream().filter(e->e instanceof Op.Result ).map(e->(Op.Result)e),
277 result -> recurse(result.op())
278 )
279 );
280 return self();
281 }
282
283 @Override
284 public final T labeledOp( JavaOp.LabeledOp labeledOp) {
285 var labelNameOp = labeledOp.bodies().getFirst().entryBlock().ops().getFirst();
286 CoreOp.ConstantOp constantOp = (CoreOp.ConstantOp) labelNameOp;
287 literal(constantOp.value().toString()).colon().nl();
288 var forLoopOp = labeledOp.bodies().getFirst().entryBlock().ops().get(1);
289 recurse(forLoopOp);
290 return self();
291 }
292
293 @Override
294 public final T breakOp( JavaOp.BreakOp breakOp) {
295 breakKeyword();
296 if (!breakOp.operands().isEmpty() && breakOp.operands().getFirst() instanceof Op.Result result) {
297 sp();
298 if (result.op() instanceof CoreOp.ConstantOp c) {
299 literal(c.value().toString());
300 }
301 }
302 return self();
303 }
304
305 @Override
306 public final T continueOp( JavaOp.ContinueOp continueOp) {
307 if (!continueOp.operands().isEmpty()
308 && continueOp.operands().getFirst() instanceof Op.Result result
309 && result.op() instanceof CoreOp.ConstantOp c
310 ) {
311 continueKeyword().sp().literal(c.value().toString());
312 } else if (scopedCodeBuilderContext().isInFor()) {
313 // nope
314 } else {
315 continueKeyword();
316 }
317
318 return self();
319 }
320
321 @Override
322 public final T ifOp( JavaOp.IfOp ifOp) {
323 scopedCodeBuilderContext().ifScope(ifOp, () -> {
324 var lastWasBody = Mutable.of(false);
325 var i = Mutable.of(0);
326 // We probably should just use a regular for loop here ;)
327 ifOp.bodies().forEach(b->{
328 int idx = i.get();
329 if (b.yieldType() instanceof JavaType javaType && javaType == JavaType.VOID) {
330 if (ifOp.bodies().size() > idx && ifOp.bodies().get(idx).entryBlock().ops().size() > 1){
331 if (lastWasBody.get()) {
332 elseKeyword();
333 }
334 braceNlIndented(_ ->
335 nlSeparated(OpHelper.Statement.statements(ifOp.bodies().get(idx).entryBlock()),
336 root-> statement(root)
337 ));
338 }
339 lastWasBody.set(true);
340 } else {
341 when(idx>0,_-> elseKeyword().sp());
342 ifKeyword().paren(_ ->
343 ifOp.bodies().get(idx).entryBlock() // get the entryblock if bodies[c.value]
344 .ops().stream().filter(o->o instanceof CoreOp.YieldOp) // we want all the yields
345 .forEach((yield) -> recurse( yield))
346 );
347 lastWasBody.set(false);
348 }
349 i.set(i.get()+1);
350 });
351 });
352 return self();
353 }
354
355 @Override
356 public final T whileOp( JavaOp.WhileOp whileOp) {
357 whileKeyword().paren(_ ->
358 OpHelper.entryBlockOfBodyN(whileOp, 0)
359 .ops().stream().filter(o -> o instanceof CoreOp.YieldOp)
360 .forEach(o -> recurse( o))
361 );
362 braceNlIndented(_ ->
363 nlSeparated(OpHelper.Statement.bodyStatements(whileOp.loopBody()),
364 statement->statement(statement)
365 )
366 );
367 return self();
368 }
369
370 @Override
371 public final T forOp( JavaOp.ForOp forOp) {
372 scopedCodeBuilderContext().forScope(forOp, () ->
373 forKeyword().paren(_ -> {
374 forOp.initBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
375 semicolon().sp();
376 forOp.condBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
377 semicolon().sp();
378 commaSpaceSeparated(
379 OpHelper.Statement.statements(forOp.updateBody().entryBlock()),
380 op -> recurse( op)
381 );
382 }).braceNlIndented(_ ->
383 nlSeparated(OpHelper.Statement.bodyStatements(forOp.loopBody()),
384 statement ->statement(statement)
385 )
386 )
387 );
388 return self();
389 }
390
391 @Override
392 public T invokeOp( JavaOp.InvokeOp invokeOp) {
393 var invoke = invoke(scopedCodeBuilderContext().lookup(),invokeOp);
394
395 funcName(invoke.op()).paren(_ ->
396 commaSpaceSeparated(invoke.op().operands(),
397 op -> {if (op instanceof Op.Result result) {recurse( result.op());}
398 })
399 );
400
401 return self();
402 }
403
404 @Override
405 public final T conditionalExpressionOp( JavaOp.ConditionalExpressionOp ternaryOp) {
406 OpHelper.Ternary ternary = ternary(scopedCodeBuilderContext().lookup(),ternaryOp);
407 ternary.condBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
408 questionMark();
409 ternary.thenBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
410 colon();
411 ternary.elseBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
412 return self();
413 }
414
415 /**
416 * Wrap paren() of precedence of op is higher than parent.
417 *
418
419 * @param parent
420 * @param child
421 */
422 @Override
423 public final T parenthesisIfNeeded( Op parent, Op child) {
424 return parenWhen(Precedence.needsParenthesis(parent,child), _ -> recurse( child));
425 }
426
427 @Override
428 public final T returnOp( CoreOp.ReturnOp returnOp) {
429 returnKeyword().when(!returnOp.operands().isEmpty(),
430 $-> $.sp().parenthesisIfNeeded( returnOp, ((Op.Result) returnOp.operands().getFirst()).op())
431 );
432 return self();
433 }
434
435 public final T statement(Op op) {
436 recurse( op);
437 if (switch (op){
438 case JavaOp.ForOp _ -> false;
439 case JavaOp.WhileOp _ -> false;
440 case JavaOp.IfOp _ -> false;
441 case JavaOp.LabeledOp _ -> false;
442 case JavaOp.YieldOp _ -> false;
443 case CoreOp.TupleOp _ ->false;
444 default -> true;
445 }
446 ){
447 semicolon();
448 }
449 return self();
450 }
451
452 public final T declareParam( FuncOpParams.Info param){
453 return type((JavaType) param.parameter.type()).sp().varName(param.varOp);
454 }
455
456 @Override
457 public T newOp( JavaOp.NewOp newOp) {
458 newKeyword().sp().type((JavaType) newOp.resultType());
459 if (newOp.operands().isEmpty()){
460 ocparen();
461 }else {
462 if (newOp.resultType() instanceof ArrayType){
463 brace(_ -> {
464 commaSpaceSeparated(newOp.operands(),
465 op -> {
466 if (op instanceof Op.Result result) {
467 recurse( result.op());
468 }
469 });
470 });
471 }else {
472 paren(_ -> {
473 commaSpaceSeparated(newOp.operands(),
474 op -> {
475 if (op instanceof Op.Result result) {
476 recurse( result.op());
477 }
478 });
479 });
480 }
481 }
482 return self();
483 }
484 @Override
485 public T arrayLoadOp( JavaOp.ArrayAccessOp.ArrayLoadOp arrayLoadOp){
486 recurse(((Op.Result)arrayLoadOp.operands().get(0)).op());
487 sbrace(_-> recurse(((Op.Result)arrayLoadOp.operands().get(1)).op()));
488 return self();
489 }
490
491 @Override
492 public T arrayStoreOp( JavaOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp){
493 recurse(((Op.Result)arrayStoreOp.operands().get(0)).op());
494 sbrace(_-> recurse(((Op.Result)arrayStoreOp.operands().get(1)).op()));
495 sp().equals().sp();
496 recurse(((Op.Result)arrayStoreOp.operands().get(2)).op());
497 return self();
498 }
499
500 @Override
501 public T enhancedForOp(JavaOp.EnhancedForOp enhancedForOp){
502 forKeyword().paren(_-> {
503 enhancedForOp.initBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
504 sp().colon().sp().blockInlineComment("Get rid of = before this");
505 enhancedForOp.exprBody().entryBlock().ops().stream().filter(o -> o instanceof CoreOp.YieldOp).forEach(o -> recurse( o));
506 }).braceNlIndented(_->
507 nlSeparated(OpHelper.Statement.bodyStatements(enhancedForOp.loopBody()),
508 this::statement
509 )
510
511 );
512 return self();
513 }
514
515 @Override
516 public T blockOp( JavaOp.BlockOp blockOp) {
517 return braceNlIndented(_-> nlSeparated(OpHelper.Statement.statements(blockOp.body().entryBlock()), this::statement));
518 }
519
520 @Override
521 public T concatOp( JavaOp.ConcatOp concatOp) {
522 return
523 recurse( ((Op.Result)concatOp.operands().get(0)).op()).
524 add().recurse( ((Op.Result)concatOp.operands().get(1)).op());
525 }
526
527 @Override
528 public final T lambdaOp( JavaOp.LambdaOp lambdaOp) {
529 scopedCodeBuilderContext().lambdaScope(lambdaOp,()-> {
530 var parameters = lambdaOp.body().entryBlock().parameters();
531 // if (parameters.isEmpty()) {
532 // ocparen();
533 //}else{
534 parenWhen(parameters.size()!=1,_->{
535 commaSpaceSeparated(parameters,$->
536 varName((CoreOp.VarOp)$.uses().stream().findFirst().get().op())
537 );
538 });
539 //}
540 sp().rarrow().sp();
541 braceNlIndented(_ -> {
542 nlSeparated(OpHelper.Statement.bodyStatements(lambdaOp.body()),
543 op->{
544 if (op instanceof CoreOp.VarOp varOp
545 && varOp.operands().getFirst() instanceof Block.Parameter parameter
546 ){
547 varName(varOp);
548 }else {
549 statement(op);
550 }
551 }
552 );
553 });
554 }
555 );
556 return self();
557 }
558 }