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 }