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 }