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