1 /*
2 * Copyright (c) 2024-2026, 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.Op;
28 import jdk.incubator.code.dialect.core.CoreOp;
29 import jdk.incubator.code.dialect.java.JavaOp;
30 import optkl.util.Mutable;
31
32 import java.util.function.Consumer;
33 import java.util.stream.Stream;
34
35 /**
36 * Extends the base TextBuilder to add common constructs/keywords for generating C99/Java style code.
37 *
38 * @author Gary Frost
39 */
40 public abstract class CodeBuilder<T extends CodeBuilder<T>>
41 extends TextBuilder<T> implements CodeRenderer<T> {
42
43 public T semicolon() {
44 return symbol(";");
45 }
46
47 public T semicolonNl() {
48 return semicolon().nl();
49 }
50
51 public T comma() {
52 return symbol(",");
53 }
54
55 final public T commaSpace() {
56 return comma().space();
57 }
58
59 public T tilde() {
60 return symbol("~");
61 }
62
63 public T dot() {
64 return symbol(".");
65 }
66
67 public T leftShift() {
68 return symbol("<<");
69 }
70
71 public T rightShift() {
72 return symbol(">>");
73 }
74
75 public T rightShift(int v) {
76 return rightShift().intValue(v);
77 }
78
79 public T leftShift(int v) {
80 return leftShift().intValue(v);
81 }
82
83 public T equals() {
84 return symbol("=");
85 }
86
87 public T assign() {
88 return space().equals().space();
89 }
90
91 public T dollar() {
92 return symbol("$");
93 }
94
95 public T plusplus() {
96 return symbol("++");
97 }
98
99 public T plusEquals() {
100 return symbol("+=");
101 }
102
103 public T minusminus() {
104 return symbol("--");
105 }
106
107 public T ne() {
108 return pling().equals();
109 }
110
111 public T lineComment(String line) {
112 return comment("//").space().comment(line).nl();
113 }
114
115 @Override
116 public T constant(String text) {
117 return emitText(text);
118 }
119
120
121 public T blockComment(String block) {
122 return comment("/*").nl().comment(block).nl().symbol("*/").nl();
123 }
124
125 public T blockInlineComment(String block) {
126 return comment("/*").space().comment(block).space().comment("*/");
127 }
128
129 public T newKeyword() {
130 return keyword("new");
131 }
132
133
134 public T staticKeyword() {
135 return keyword("static");
136 }
137
138 public T constexprKeyword() {
139 return keyword("constexpr");
140 }
141
142 public T constKeyword() {
143 return keyword("const");
144 }
145
146 public T explicitKeyword() {
147 return keyword("explicit");
148 }
149
150 public T virtualKeyword() {
151 return keyword("virtual");
152 }
153
154 public T ifKeyword() {
155 return keyword("if");
156 }
157
158 public T whileKeyword() {
159 return keyword("while");
160 }
161
162
163 public T breakKeyword() {
164 return keyword("break");
165 }
166
167 public T gotoKeyword() {
168 return keyword("goto");
169 }
170
171 public T continueKeyword() {
172 return keyword("continue");
173 }
174
175
176 public T colon() {
177 return symbol(":");
178 }
179
180
181 public T nullConst() {
182 return symbol("NULL");
183 }
184
185
186 public T elseKeyword() {
187 return keyword("else");
188 }
189
190
191 public T returnKeyword() {
192 return keyword("return");
193 }
194
195 public T returnKeyword(String identifier) {
196 return returnKeyword().space().identifier(identifier);
197 }
198
199 public T switchKeyword() {
200 return keyword("switch");
201 }
202
203
204 public T caseKeyword() {
205 return keyword("case");
206 }
207
208
209 public T defaultKeyword() {
210 return keyword("default");
211 }
212
213 public T doKeyword() {
214 return keyword("do");
215 }
216
217 public T forKeyword() {
218 return keyword("for");
219 }
220
221 public T ampersand() {
222 return symbol("&");
223 }
224
225 public T addressOf(String identifier) {
226 return ampersand().identifier(identifier);
227 }
228
229 public T asterisk() {
230 return symbol("*");
231 }
232
233 public T dereference(String identifier) {
234 return asterisk().identifier(identifier);
235 }
236
237 public T mul() {
238 return asterisk();
239 }
240
241 public T percent() {
242 return symbol("%");
243 }
244
245 public T mod() {
246 return percent();
247 }
248
249 public T slash() {
250 return symbol("/");
251 }
252
253 public T div() {
254 return slash();
255 }
256
257 public T plus() {
258 return symbol("+");
259 }
260
261 public T add() {
262 return plus();
263 }
264
265 public T minus() {
266 return symbol("-");
267 }
268
269 public T sub() {
270 return minus();
271 }
272
273 public T lt() {
274 return symbol("<");
275 }
276
277 public T eq() {
278 return equals().equals();
279 }
280
281 public T lte() {
282 return lt().equals();
283 }
284
285 public T gte() {
286 return gt().equals();
287 }
288
289 public T pling() {
290 return symbol("!");
291 }
292
293 public T gt() {
294 return symbol(">");
295 }
296
297 public T condAnd() {
298 return symbol("&&");
299 }
300
301 public T condOr() {
302 return symbol("||");
303 }
304
305 public T oparen() {
306 return symbol("(");
307 }
308
309 public final T paren(Consumer<T> consumer) {
310 return oparen().accept(consumer).cparen();
311 }
312
313 public T ocparen() {
314 return oparen().cparen();
315 }
316
317 public T parenWhen(boolean value, Consumer<T> consumer) {
318 if (value) {
319 oparen().accept(consumer).cparen();
320 } else {
321 accept(consumer);
322 }
323 return self();
324 }
325
326 public T semicolonTerminated(Consumer<T> consumer) {
327 return accept(consumer).semicolon();
328 }
329
330 public T semicolonNlTerminated(Consumer<T> consumer) {
331 return semicolonTerminated(consumer).nl();
332 }
333
334 public T obrace() {
335 return symbol("{");
336 }
337
338 public T indent(Consumer<T> ct) {
339 return in().accept(ct).out();
340 }
341
342 public T nlIndentNl(Consumer<T> ct) {
343 return nl().indent(ct).nl();
344 }
345
346 public T braceNlIndented(Consumer<T> ct) {
347 return obrace().nlIndentNl(ct).cbrace();
348 }
349
350 public T parenNlIndented(Consumer<T> ct) {
351 return oparen().nlIndentNl(ct).cparen();
352 }
353
354 public T brace(Consumer<T> ct) {
355 return obrace().indent(ct).cbrace();
356 }
357
358 public T ocsbrace() {
359 return osbrace().csbrace();
360 }
361
362 public T ocbrace() {
363 return obrace().cbrace();
364 }
365
366 public T sbrace(Consumer<T> ct) {
367 return osbrace().accept(ct).csbrace();
368 }
369
370 public T accept(Consumer<T> ct) {
371 ct.accept(self());
372 return self();
373 }
374
375
376 public T ochevron() {
377 return rawochevron();
378 }
379
380 final public T rawochevron() {
381 return emitText("<");
382 }
383
384 public T bitwiseOR() {
385 return symbol("|");
386 }
387
388 public T cchevron() {
389 return rawcchevron();
390 }
391
392 public T chevron(Consumer<T> ct) {
393 return rawochevron().indent(ct).rawcchevron();
394 }
395
396 final public T rawcchevron() {
397 return emitText(">");
398 }
399
400 public T osbrace() {
401 return symbol("[");
402 }
403
404 public T cparen() {
405 return symbol(")");
406 }
407
408 public T cbrace() {
409 return symbol("}");
410 }
411
412
413 public T csbrace() {
414 return symbol("]");
415 }
416
417 public T underscore() {
418 return symbol("_");
419 }
420
421 public T dquote() {
422 return symbol("\"");
423 }
424
425 public T odquote() {
426 return dquote();
427 }
428
429 public T cdquote() {
430 return dquote();
431 }
432
433 public T squote() {
434 return symbol("'");
435 }
436
437 public T osquote() {
438 return squote();
439 }
440
441 public T csquote() {
442 return squote();
443 }
444
445 public T dquote(String string) {
446 return odquote().escaped(string).cdquote();
447 }
448
449 public T at() {
450 return symbol("@");
451 }
452
453 public T hat() {
454 return symbol("^");
455 }
456
457 public T squote(String txt) {
458 return osquote().escaped(txt).csquote();
459 }
460
461 public T rarrow() {
462 return symbol("->");
463 }
464
465 public T larrow() {
466 return symbol("<-");
467 }
468
469
470 public T questionMark() {
471 return symbol("?");
472 }
473
474 public T hash() {
475 return symbol("#");
476 }
477
478 public T when(boolean c, Consumer<T> consumer) {
479 if (c) {
480 accept(consumer);
481 }
482 return self();
483 }
484
485 public T either(boolean c, Consumer<T> lhs, Consumer<T> rhs) {
486 if (c) {
487 accept(lhs);
488 } else {
489 accept(rhs);
490 }
491 return self();
492 }
493
494 public <I> T separated(Iterable<I> iterable, Consumer<T> separator, Consumer<I> consumer) {
495 var first = Mutable.of(true);
496 iterable.forEach(t -> {
497 if (first.get()) {
498 first.set(false);
499 } else {
500 separator.accept(self());
501 }
502 consumer.accept(t);
503 });
504 return self();
505 }
506
507 public <I> T commaSpaceSeparated(Iterable<I> iterable, Consumer<I> consumer) {
508 return separated(iterable, _ -> commaSpace(), consumer);
509 }
510
511 public T commaSpaceSeparated(Consumer<T>... consumers) {
512 for (int i = 0; i < consumers.length; i++) {
513 if (i > 0) {
514 commaSpace();
515 }
516 consumers[i].accept(self());
517 }
518 return self();
519 }
520
521 public T args(Consumer<T>... consumers) {
522 return commaSpaceSeparated(consumers);
523 }
524
525 public <I> T commaSeparated(Iterable<I> iterable, Consumer<I> consumer) {
526 return separated(iterable, _ -> comma(), consumer);
527 }
528
529 public <I> T commaNlSeparated(Iterable<I> iterable, Consumer<I> consumer) {
530 return separated(iterable, _ -> comma().nl(), consumer);
531 }
532
533 public <I> T barSeparated(Iterable<I> iterable, Consumer<I> consumer) {
534 return separated(iterable, _ -> bitwiseOR(), consumer);
535 }
536
537 public <I> T semicolonNlSeparated(Iterable<I> iterable, Consumer<I> consumer) {
538 return separated(iterable, _ -> semicolonNl(), consumer);
539 }
540
541 public <I> T nlSeparated(Iterable<I> iterable, Consumer<I> consumer) {
542 return separated(iterable, _ -> nl(), consumer);
543 }
544
545 public <I> T separated(Stream<I> stream, Consumer<T> separator, Consumer<I> consumer) {
546 var first = Mutable.of(true);
547 stream.forEach(t -> {
548 if (first.get()) {
549 first.set(false);
550 } else {
551 separator.accept(self());
552 }
553 consumer.accept(t);
554 });
555 return self();
556 }
557
558 public <I> T commaSpaceSeparated(Stream<I> stream, Consumer<I> consumer) {
559 return separated(stream, _ -> commaSpace(), consumer);
560 }
561
562 public <I> T commaSeparated(Stream<I> stream, Consumer<I> consumer) {
563 return separated(stream, _ -> comma(), consumer);
564 }
565
566 public <I> T nlSeparated(Stream<I> stream, Consumer<I> consumer) {
567 return separated(stream, _ -> nl(), consumer);
568 }
569
570 public final T s32Type() {
571 return typeName("int");
572 }
573
574 public final T s32Type(String identifier) {
575 return s32Type().space().identifier(identifier);
576 }
577
578 public final T intConstZero() {
579 return constant("0");
580 }
581
582 public final T intConstOne() {
583 return constant("1");
584 }
585
586 public final T intConstTwo() {
587 return constant("2");
588 }
589
590 public final T voidType() {
591 return typeName("void");
592 }
593
594 public final T s08Type() {
595 return typeName("char");
596 }
597
598 public final T s08Type(String name) {
599 return s08Type().space().identifier(name);
600 }
601
602 public final T f32Type() {
603 return typeName("float");
604 }
605
606 public final T f32Type(String identifier) {
607 return f32Type().space().identifier(identifier);
608 }
609
610 public final T s64Type() {
611 return typeName("long");
612 }
613
614 public final T f64Type() {
615 return typeName("double");
616 }
617
618 public final T boolType() {
619 return typeName("char");
620 }
621
622 public final T s16Type() {
623 return typeName("short");
624 }
625
626 public final T s16Type(String identifier) {
627 return s16Type().space().identifier(identifier);
628 }
629
630
631 @Override
632 public final T comment(String text) {
633 return emitText(text);
634 }
635
636 @Override
637 public T identifier(String text) {
638 return emitText(text);
639 }
640
641 @Override
642 public T reserved(String text) {
643 return emitText(text);
644 }
645
646 @Override
647 public T label(String text) {
648 return emitText(text);
649 }
650
651 @Override
652 public final T symbol(String text) {
653 return emitText(text);
654 }
655
656 @Override
657 public final T typeName(String text) {
658 return emitText(text);
659 }
660
661 @Override
662 public final T keyword(String text) {
663 return emitText(text);
664 }
665
666 @Override
667 public final T literal(String text) {
668 return emitText(text);
669 }
670
671 @Override
672 public T nl() {
673 return super.nl();
674 }
675
676 @Override
677 public T space() {
678 return emitText(" ");
679 }
680
681 public T builtin(String text) {
682 return emitText(text);
683 }
684
685 public T composeIdentifier(String preffix, String postfix) {
686 return identifier(preffix + postfix);
687 }
688
689 public String toCamelExceptFirst(String s) {
690 String[] parts = s.split("_");
691 StringBuilder camelCaseString = new StringBuilder();
692 for (String part : parts) {
693 camelCaseString.append(camelCaseString.isEmpty()
694 ? part.toLowerCase()
695 : part.substring(0, 1).toUpperCase() + part.substring(1).toLowerCase());
696 }
697 return camelCaseString.toString();
698 }
699
700 public final T sizeArray(int size) {
701 return sbrace( _ -> constant(Integer.toString(size)));
702 }
703
704 public final T oracleCopyright(){
705 return blockComment("""
706 * Copyright (c) 2025-2026, Oracle and/or its affiliates. All rights reserved.
707 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
708 *
709 * This code is free software; you can redistribute it and/or modify it
710 * under the terms of the GNU General Public License version 2 only, as
711 * published by the Free Software Foundation. Oracle designates this
712 * particular file as subject to the "Classpath" exception as provided
713 * by Oracle in the LICENSE file that accompanied this code.
714 *
715 * This code is distributed in the hope that it will be useful, but WITHOUT
716 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
717 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
718 * version 2 for more details (a copy is included in the LICENSE file that
719 * accompanied this code).
720 *
721 * You should have received a copy of the GNU General Public License version
722 * 2 along with this work; if not, write to the Free Software Foundation,
723 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
724 *
725 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
726 * or visit www.oracle.com if you need additional information or have any
727 * questions."""
728 );
729 }
730 public final T varName(String name) {
731 return identifier(name);
732 }
733
734 public final T varName(CoreOp.VarOp varOp) {
735 return varName(varOp.varName());
736 }
737 public final T varName(CoreOp.VarAccessOp.VarLoadOp varOp) {
738 blockInlineComment(varOp.toString());
739 return self();
740 }
741 public final T funcName(CoreOp.FuncCallOp funcCallOp){
742 return identifier(funcCallOp.funcName());
743 }
744 public final T funcName(CoreOp.FuncOp funcOp) {
745 return identifier(funcOp.funcName());
746 }
747 public final T fieldName(JavaOp.FieldAccessOp fieldAccessOp) {
748 return identifier(fieldAccessOp.fieldDescriptor().name());
749 }
750 public final T funcName(JavaOp.InvokeOp invokeOp){
751 return identifier(invokeOp.invokeDescriptor().name());
752 }
753
754
755 protected final T camel(String value) {
756 return identifier(Character.toString(Character.toLowerCase(value.charAt(0)))).identifier(value.substring(1));
757 }
758
759 public final T camelJoin(String prefix, String suffix) {
760 return camel(prefix).identifier(Character.toString(Character.toUpperCase(suffix.charAt(0)))).identifier(suffix.substring(1));
761 }
762
763 public T symbol(Op op) {
764 return switch (op) {
765 case JavaOp.ModOp o -> percent();
766 case JavaOp.MulOp o -> mul();
767 case JavaOp.DivOp o -> div();
768 case JavaOp.AddOp o -> add();
769 case JavaOp.SubOp o -> sub();
770 case JavaOp.LtOp o -> lt();
771 case JavaOp.GtOp o -> gt();
772 case JavaOp.LeOp o -> lte();
773 case JavaOp.GeOp o -> gte();
774 case JavaOp.AshrOp o -> cchevron().cchevron();
775 case JavaOp.LshlOp o -> ochevron().ochevron();
776 case JavaOp.LshrOp o -> cchevron().cchevron();
777 case JavaOp.NeqOp o -> pling().equals();
778 case JavaOp.NegOp o -> minus();
779 case JavaOp.EqOp o -> equals().equals();
780 case JavaOp.NotOp o -> pling();
781 case JavaOp.AndOp o -> ampersand();
782 case JavaOp.OrOp o -> bitwiseOR();
783 case JavaOp.XorOp o -> hat();
784 case JavaOp.ConditionalAndOp o -> condAnd();
785 case JavaOp.ConditionalOrOp o -> condOr();
786 default -> throw new IllegalStateException("Unexpected value: " + op);
787 };
788 }
789 }