1 /*
  2  * Copyright (c) 2025, 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 
 26 package oracle.code.onnx.opgen;
 27 
 28 import jdk.incubator.code.extern.ExternalizedTypeElement;
 29 import oracle.code.onnx.OpSchema;
 30 
 31 import java.io.*;
 32 import java.nio.file.Path;
 33 import java.util.*;
 34 
 35 import static java.util.Comparator.comparing;
 36 import static java.util.stream.Collectors.groupingBy;
 37 import static java.util.stream.Collectors.toCollection;
 38 
 39 public class OpGen {
 40 
 41     final SortedMap<String, SortedSet<OpSchema>> schemas;
 42 
 43     OpGen(List<OpSchema> schemas) {
 44         this.schemas = schemas.stream().collect(groupingBy(
 45                 OpSchema::name,
 46                 TreeMap::new,
 47                 toCollection(() -> new TreeSet<>(comparing(OpSchema::since_version).reversed())
 48                 )));
 49     }
 50 
 51     static final String ONNX_PACKAGE = "oracle.code.onnx";
 52     static final String ONNX_IR_PACKAGE = ONNX_PACKAGE + ".ir";
 53     static final String ONNX_OPS_CLASS = "OnnxOps";
 54 
 55     void genOpsClass(Path dir) throws IOException {
 56         OutputStreamWriter osw = new OutputStreamWriter(
 57                 new FileOutputStream(dir.resolve(ONNX_OPS_CLASS + ".java").toFile()));
 58         genOpsClass(osw);
 59     }
 60 
 61     void genOpsClass(Writer w_) throws IOException {
 62         IndentWriter w = new IndentWriter(w_);
 63 
 64         w.write("""
 65                 /*
 66                  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
 67                  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 68                  *
 69                  * This code is free software; you can redistribute it and/or modify it
 70                  * under the terms of the GNU General Public License version 2 only, as
 71                  * published by the Free Software Foundation.  Oracle designates this
 72                  * particular file as subject to the "Classpath" exception as provided
 73                  * by Oracle in the LICENSE file that accompanied this code.
 74                  *
 75                  * This code is distributed in the hope that it will be useful, but WITHOUT
 76                  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 77                  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 78                  * version 2 for more details (a copy is included in the LICENSE file that
 79                  * accompanied this code).
 80                  *
 81                  * You should have received a copy of the GNU General Public License version
 82                  * 2 along with this work; if not, write to the Free Software Foundation,
 83                  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 84                  *
 85                  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 86                  * or visit www.oracle.com if you need additional information or have any
 87                  * questions.
 88                  */
 89                 """);
 90         w.write("// Auto-generated from ONNX op schema\n");
 91         w.write("\n");
 92         w.write("package " + ONNX_IR_PACKAGE + ";\n");
 93         w.write("\n");
 94         w.write("""
 95                 import jdk.incubator.code.*;
 96                 import jdk.incubator.code.extern.ExternalizedOp;
 97                 import jdk.incubator.code.extern.OpFactory;
 98 
 99                 import java.util.*;
100                 """);
101         w.write("import " + ONNX_PACKAGE + ".Tensor;\n");
102         w.write("\n");
103 
104         w.write("@SuppressWarnings({\"OptionalUsedAsFieldOrParameterType\", \"unused\", \"SequencedCollectionMethodCanBeUsed\"})\n");
105         w.write("public final class " + ONNX_OPS_CLASS + " extends ExplicitOnnxOps {\n");
106 
107         w.in();
108 
109         w.write("\n");
110         w.write("private " + ONNX_OPS_CLASS + "() {}\n");
111         w.write("\n");
112 
113         w.out();
114         for (OpSchema s : schemas.values().stream().map(SortedSet::getFirst).toList()) {
115             try {
116                 if (skip(s)) {
117                     System.out.println("Skipping " + s.name());
118                     continue;
119                 }
120 
121                 String g = genOpClass(s);
122                 w.write(g);
123                 w.write("\n");
124             } catch (UnsupportedOperationException e) {
125                 System.err.println("Skipping " + s.name() + ": " + e.getMessage());
126             }
127         }
128 
129         w.write("}\n");
130         w.flush();
131     }
132 
133     private boolean skip(OpSchema s) {
134         return s.attributes().stream().anyMatch(a ->
135                 a.type() == OpSchema.AttributeType.GRAPH ||
136                         a.type() == OpSchema.AttributeType.GRAPHS);
137     }
138 
139     private String genOpClass(OpSchema s) throws IOException {
140         StringWriter sw = new StringWriter();
141         IndentWriter w = new IndentWriter(sw, 4);
142 
143         w.write("@OpFactoryHelper.OpDeclaration(" + s.name() + ".NAME)\n");
144         w.write("public static final class " + s.name() + " extends OnnxOp {\n");
145         w.in();
146 
147         w.write("public static final String NAME = \"" + s.name() + "\";\n");
148         w.write("\n");
149 
150         genAttributeEnum(w, s);
151 
152         Map<String, List<ExternalizedTypeElement>> typeConstraints =
153                 genTypeConstraintEnum(w, s);
154 
155         genInputParameterEnum(w, s, typeConstraints);
156 
157         genOutputParameterEnum(w, s, typeConstraints);
158 
159         genSchemaInstance(w, s);
160 
161         genConstructors(w, s);
162 
163         genMethods(w, s);
164 
165         w.out();
166         w.write("}\n");
167         w.write("\n");
168 
169         genFactoryMethod(w, s);
170 
171         return sw.toString();
172     }
173 
174     private void genAttributeEnum(IndentWriter w, OpSchema s) throws IOException {
175         if (s.attributes().isEmpty()) {
176             w.write("public enum Attribute implements OnnxAttribute.None { }\n");
177             w.write("\n");
178             return;
179         }
180 
181         w.write("public enum Attribute implements OnnxAttribute {\n");
182         w.in();
183 
184         for (OpSchema.Attribute a : s.attributes()) {
185             w.write(a.name());
186             w.write("(");
187             w.write(toBoxType(a.type().type()).getSimpleName() + ".class");
188             w.write(", ");
189             w.write(Boolean.toString(!a.required()));
190             w.write(", ");
191 
192             if (!a.required() && a.default_value() != null) {
193                 switch (a.type()) {
194                     case FLOAT -> w.write(Float.toString((Float) a.default_value()) + "f");
195                     case INT -> w.write(Integer.toString((Integer) a.default_value()));
196                     case STRING -> w.write("\"" + a.default_value() + "\"");
197                     default -> throw new IllegalStateException();
198                 }
199             } else {
200                 w.write("null");
201             }
202 
203             w.write("),\n");
204         }
205         w.write(";\n");
206         w.write("\n");
207 
208         w.write("""
209                     final Class<?> t;
210                     final boolean optional;
211                     final Object defaultValue;
212 
213                     Attribute(Class<?> type, boolean optional, Object defaultValue) {
214                         this.t = type;
215                         this.optional = optional;
216                         this.defaultValue = defaultValue;
217                         assert optional || defaultValue == null;
218                     }
219 
220                     public Class<?> type() {
221                         return t;
222                     }
223 
224                     public boolean isOptional() {
225                         return optional;
226                     }
227 
228                     public Object defaultValue() {
229                         return defaultValue;
230                     }
231                 """);
232 
233         w.out();
234         w.write("}\n");
235         w.write("\n");
236     }
237 
238     private Map<String, List<ExternalizedTypeElement>> genTypeConstraintEnum(IndentWriter w, OpSchema s) throws IOException {
239         if (s.type_constraints().isEmpty()) {
240             w.write("public enum TypeConstraint implements OnnxTypeConstraint.None { }\n");
241             w.write("\n");
242             return Map.of();
243         }
244 
245         Map<String, List<ExternalizedTypeElement>> typeConstraints = new HashMap<>();
246 
247         w.write("public enum TypeConstraint implements OnnxTypeConstraint {\n");
248         w.in();
249 
250         for (OpSchema.TypeConstraintParam tcp : s.type_constraints()) {
251             List<ExternalizedTypeElement> types = tcp.allowed_type_strs().stream()
252                     .map(OpGen::parseTypeString)
253                     .toList();
254             typeConstraints.put(tcp.type_param_str(), types);
255 
256             w.write(tcp.type_param_str() + "(");
257 
258             w.write("new OnnxType.TypeVariable(");
259             w.write("\"" + tcp.type_param_str() + "\", ");
260             w.write("List.of(");
261             genTypes(w, types);
262             w.write(")");
263             w.write(")");
264 
265             w.write("),\n");
266         }
267         w.write(";\n");
268         w.write("\n");
269 
270         w.write("""
271                 final OnnxType.TypeVariable typeVariable;
272 
273                 TypeConstraint(OnnxType.TypeVariable typeVariable) {
274                     assert typeVariable.name().equals(name());
275                     this.typeVariable = typeVariable;
276                 }
277 
278                 @Override
279                 public OnnxType.TypeVariable typeVariable() {
280                     return typeVariable;
281                 }
282                 """);
283 
284         w.out();
285         w.write("}\n");
286         w.write("\n");
287 
288         return typeConstraints;
289     }
290 
291     private void genInputParameterEnum(IndentWriter w, OpSchema s,
292                                        Map<String, List<ExternalizedTypeElement>> typeConstraints) throws IOException {
293         if (s.inputs().isEmpty()) {
294             w.write("public enum InputParameter implements OnnxParameter.None { }\n");
295             w.write("\n");
296             return;
297         }
298 
299         w.write("public enum InputParameter implements OnnxParameter {\n");
300         w.in();
301 
302         for (OpSchema.FormalParameter input : s.inputs()) {
303             w.write(input.name() + "(");
304 
305             if (typeConstraints.containsKey(input.type_str())) {
306                 w.write("TypeConstraint." + input.type_str() + ".typeVariable()");
307             } else {
308                 ExternalizedTypeElement type = parseTypeString(input.type_str());
309                 genType(w, type);
310             }
311             w.write(", ");
312             w.write("Quantifier.");
313             switch (input.option()) {
314                 case Single -> {
315                     w.write("REQUIRED");
316                 }
317                 case Optional -> {
318                     w.write("OPTIONAL");
319                 }
320                 case Variadic -> {
321                     w.write("VARIADIC");
322                 }
323             }
324 
325             w.write("),\n");
326         }
327         w.write(";\n");
328         w.write("\n");
329 
330         w.write("""
331                 final OnnxType type;
332                 final Quantifier quantifier;
333 
334                 InputParameter(OnnxType type, Quantifier quantifier) {
335                     this.type = type;
336                     this.quantifier = quantifier;
337                 }
338 
339                 @Override
340                 public OnnxType type() {
341                     return type;
342                 }
343 
344                 @Override
345                 public Quantifier quantifier() {
346                     return quantifier;
347                 }
348                 """);
349 
350         w.out();
351         w.write("}\n");
352         w.write("\n");
353     }
354 
355     private void genOutputParameterEnum(IndentWriter w, OpSchema s,
356                                         Map<String, List<ExternalizedTypeElement>> typeConstraints) throws IOException {
357         if (s.outputs().isEmpty()) {
358             w.write("public enum OutputParameter implements OnnxParameter.None { }\n");
359             w.write("\n");
360             return;
361         }
362 
363         w.write("public enum OutputParameter implements OnnxParameter {\n");
364         w.in();
365 
366         for (OpSchema.FormalParameter output : s.outputs()) {
367             w.write(output.name() + "(");
368 
369             if (typeConstraints.containsKey(output.type_str())) {
370                 w.write("TypeConstraint." + output.type_str() + ".typeVariable()");
371             } else {
372                 ExternalizedTypeElement type = parseTypeString(output.type_str());
373                 genType(w, type);
374             }
375             w.write(", ");
376             w.write("Quantifier.");
377             switch (output.option()) {
378                 case Single -> {
379                     w.write("REQUIRED");
380                 }
381                 case Optional -> {
382                     w.write("OPTIONAL");
383                 }
384                 case Variadic -> {
385                     w.write("VARIADIC");
386                 }
387             }
388 
389             w.write("),\n");
390         }
391         w.write(";\n");
392         w.write("\n");
393 
394         w.write("""
395                 final OnnxType type;
396                 final Quantifier quantifier;
397 
398                 OutputParameter(OnnxType type, Quantifier quantifier) {
399                     this.type = type;
400                     this.quantifier = quantifier;
401                 }
402 
403                 @Override
404                 public OnnxType type() {
405                     return type;
406                 }
407 
408                 @Override
409                 public Quantifier quantifier() {
410                     return quantifier;
411                 }
412                 """);
413 
414         w.out();
415         w.write("}\n");
416         w.write("\n");
417     }
418 
419     private void genSchemaInstance(IndentWriter w, OpSchema s) throws IOException {
420         w.write("""
421                 public static final OnnxSchema SCHEMA = new OnnxSchemaRecord(
422                         NAME,
423                         List.of(Attribute.values()),
424                         List.of(TypeConstraint.values()),
425                         List.of(InputParameter.values()),
426                         List.of(OutputParameter.values())
427                 );
428                 """);
429         w.write("\n");
430     }
431 
432     private void genConstructors(IndentWriter w, OpSchema s) throws IOException {
433         w.write("public " + s.name() + "(ExternalizedOp def) {\n");
434         w.in();
435         w.write("super(SCHEMA, def);\n");
436         w.out();
437         w.write("}\n");
438         w.write("\n");
439 
440         w.write(s.name() + "(" + s.name() + " that, CopyContext cc) {\n");
441         w.write("    super(that, cc);\n");
442         w.write("}\n");
443         w.write("\n");
444 
445         w.write("@Override\n");
446         w.write("public " + s.name() + " transform(CopyContext cc, OpTransformer ot) {\n");
447         w.write("    return new " + s.name() + "(this, cc);\n");
448         w.write("}\n");
449         w.write("\n");
450 
451 
452         w.write(s.name() + "(");
453 
454         // Result type parameter
455         w.write("TypeElement resultType, ");
456 
457         boolean hasOptionalOutputs = s.outputs()
458                 .stream().anyMatch(o -> o.option() == OpSchema.FormalParameterOption.Optional);
459         if (hasOptionalOutputs) {
460             w.write("Set<OutputParameter> optionalOutputs, ");
461         }
462 
463         boolean first = true;
464         for (OpSchema.FormalParameter inParam : s.inputs()) {
465             if (!first) {
466                 w.write(", ");
467             }
468 
469             switch (inParam.option()) {
470                 case Single -> {
471                     w.write("Value");
472                 }
473                 case Optional -> {
474                     w.write("java.util.Optional<Value>");
475                 }
476                 case Variadic -> {
477                     w.write("List<Value>");
478                 }
479             }
480             w.write(" ");
481             w.write(inParam.name());
482 
483             first = false;
484         }
485 
486         for (OpSchema.Attribute attribute : s.attributes()) {
487             if (!first) {
488                 w.write(", ");
489             }
490 
491             OpSchema.AttributeType aType = attribute.type();
492             String typeString = switch (aType) {
493                 default -> {
494                     if (attribute.required()) {
495                         yield aType.type().getSimpleName();
496                     } else {
497                         yield toBoxType(aType.type()).getSimpleName();
498                     }
499                 }
500             };
501             if (attribute.required()) {
502                 w.write(typeString);
503             } else {
504                 w.write("java.util.Optional<");
505                 w.write(typeString);
506                 w.write(">");
507             }
508             w.write(" ");
509             w.write(attribute.name());
510 
511             first = false;
512         }
513 
514         w.write(") {\n");
515         w.in();
516 
517         w.write("super(SCHEMA, resultType, ");
518 
519         if (hasOptionalOutputs) {
520             w.write("optionalOutputs, ");
521         } else {
522             w.write("Set.of(), ");
523         }
524 
525         w.write("List.of(");
526         first = true;
527         for (OpSchema.FormalParameter inParam : s.inputs()) {
528             if (!first) {
529                 w.write(", ");
530             }
531             w.write(inParam.name());
532 
533             first = false;
534         }
535         w.write("), ");
536 
537         w.write("List.of(");
538         first = true;
539         for (OpSchema.Attribute a : s.attributes()) {
540             if (!first) {
541                 w.write(", ");
542             }
543             w.write(a.name());
544 
545             first = false;
546         }
547         w.write(")");
548 
549         w.write(");\n");
550 
551         w.out();
552         w.write("}\n");
553         w.write("\n");
554     }
555 
556     private void genMethods(IndentWriter w, OpSchema s) throws IOException {
557         genResultTypeMethod(w, s);
558 
559         genOutputParameterMethods(w, s);
560 
561         genInputParameterMethods(w, s);
562 
563         genAttributeAccessMethods(w, s);
564     }
565 
566     private static void genResultTypeMethod(IndentWriter w, OpSchema s) throws IOException {
567     }
568 
569     private static void genOutputParameterMethods(IndentWriter w, OpSchema s) throws IOException {
570         w.write("""
571                 @Override
572                 public SequencedSet<OnnxParameter> onnxOutputs() {
573                     return onnxOutputs(SCHEMA);
574                 }
575                 """);
576         w.write("\n");
577     }
578 
579     private static void genInputParameterMethods(IndentWriter w, OpSchema s) throws IOException {
580         w.write("""
581                 @Override
582                 public SequencedMap<OnnxParameter, Object> onnxInputs() {
583                 """);
584         w.in();
585 
586         w.write("return onnxInputs(SCHEMA, ");
587         w.write("List.of(");
588         boolean first = true;
589         for (OpSchema.FormalParameter p : s.inputs()) {
590             if (!first) {
591                 w.write(", ");
592             }
593 
594             w.write(p.name() + "()");
595 
596             first = false;
597             ;
598         }
599         w.write(")");
600         w.write(");\n");
601 
602         w.out();
603         w.write("}\n");
604         w.write("\n");
605 
606 
607         int i = 0;
608         int rc = 0;
609         int oc = 0;
610         for (OpSchema.FormalParameter p : s.inputs()) {
611             w.write("public ");
612             switch (p.option()) {
613                 case Single -> {
614                     w.write("Value ");
615                     rc++;
616                 }
617                 case Optional -> {
618                     w.write("java.util.Optional<Value> ");
619                     oc++;
620                 }
621                 case Variadic -> {
622                     w.write("List<Value> ");
623                 }
624             }
625             w.write(p.name() + "() {\n");
626             w.in();
627 
628             switch (p.option()) {
629                 case Single -> {
630                     w.write("return operands().get(" + (i++) + ");\n");
631                 }
632                 case Optional -> {
633                     w.write("int i = optionalInputArguments.indexOf(InputParameter." + p.name() + ");\n");
634                     w.write("return i != -1 ? java.util.Optional.of(operands().get(" + rc + " + i)) : java.util.Optional.empty();\n");
635                 }
636                 case Variadic -> {
637                     if (oc > 0) {
638                         w.write("int i = " + rc + " + optionalInputArguments.size();\n");
639                         w.write("return operands().subList(i, operands().size());\n");
640                     } else if (rc > 0) {
641                         w.write("return operands().subList(" + rc + ", operands().size());\n");
642                     } else {
643                         w.write("return operands();\n");
644                     }
645                 }
646             }
647 
648             w.out();
649             w.write("}\n");
650             w.write("\n");
651         }
652     }
653 
654     private static void genAttributeAccessMethods(IndentWriter w, OpSchema s) throws IOException {
655         for (OpSchema.Attribute a : s.attributes()) {
656             w.write("public ");
657 
658             OpSchema.AttributeType aType = a.type();
659             String typeString = switch (aType) {
660                 default -> {
661                     if (a.required()) {
662                         yield aType.type().getSimpleName();
663                     } else {
664                         yield toBoxType(aType.type()).getSimpleName();
665                     }
666                 }
667             };
668 
669             if (a.required()) {
670                 w.write(typeString);
671             } else {
672                 w.write("java.util.Optional<");
673                 w.write(typeString);
674                 w.write(">");
675             }
676             w.write(" ");
677             w.write(a.name() + "() {\n");
678             w.in();
679 
680             // @@@ sub-graphs have inputs and outputs
681             String typeLiteralString = toBoxType(aType.type()).getSimpleName();
682 
683             w.write(typeString + " ");
684             w.write(a.name() + " = ");
685             w.write("Attribute." + a.name() + ".access(");
686             w.write(typeLiteralString + ".class, onnxAttributes");
687             w.write(");\n");
688 
689             w.write("return ");
690             if (a.required()) {
691                 w.write(a.name());
692                 if (aType.type().isArray()) {
693                     w.write(".clone()");
694                 }
695             } else {
696                 w.write("java.util.Optional.ofNullable(" + a.name() + ")");
697                 if (aType.type().isArray()) {
698                     w.write(".map(" + typeString + "::clone)");
699                 }
700             }
701             w.write(";\n");
702 
703             w.out();
704             w.write("}\n");
705             w.write("\n");
706         }
707     }
708 
709     private void genFactoryMethod(IndentWriter w, OpSchema s) throws IOException {
710         w.write("public static " + s.name() + " " + s.name() + "(");
711 
712         // Result type parameter
713         w.write("TypeElement resultType, ");
714 
715         boolean hasOptionalOutputs = s.outputs()
716                 .stream().anyMatch(o -> o.option() == OpSchema.FormalParameterOption.Optional);
717         if (hasOptionalOutputs) {
718             w.write("Set<" + s.name() + ".OutputParameter> optionalOutputs, ");
719         }
720 
721         boolean first = true;
722         for (OpSchema.FormalParameter inParam : s.inputs()) {
723             if (!first) {
724                 w.write(", ");
725             }
726 
727             switch (inParam.option()) {
728                 case Single -> {
729                     w.write("Value");
730                 }
731                 case Optional -> {
732                     w.write("java.util.Optional<Value>");
733                 }
734                 case Variadic -> {
735                     w.write("List<Value>");
736                 }
737             }
738             w.write(" ");
739             w.write(inParam.name());
740 
741             first = false;
742         }
743 
744         for (OpSchema.Attribute attribute : s.attributes()) {
745             if (!first) {
746                 w.write(", ");
747             }
748 
749             OpSchema.AttributeType aType = attribute.type();
750             String typeString = switch (aType) {
751                 default -> {
752                     if (attribute.required()) {
753                         yield aType.type().getSimpleName();
754                     } else {
755                         yield toBoxType(aType.type()).getSimpleName();
756                     }
757                 }
758             };
759             if (attribute.required()) {
760                 w.write(typeString);
761             } else {
762                 w.write("java.util.Optional<");
763                 w.write(typeString);
764                 w.write(">");
765             }
766             w.write(" ");
767             w.write(attribute.name());
768 
769             first = false;
770         }
771 
772         w.write(") {\n");
773         w.in();
774 
775         w.write("return new " + s.name() + "(");
776 
777         w.write("resultType, ");
778 
779         if (hasOptionalOutputs) {
780             w.write("optionalOutputs, ");
781         }
782 
783         first = true;
784         for (OpSchema.FormalParameter inParam : s.inputs()) {
785             if (!first) {
786                 w.write(", ");
787             }
788 
789             w.write(inParam.name());
790 
791             first = false;
792         }
793 
794         for (OpSchema.Attribute attribute : s.attributes()) {
795             if (!first) {
796                 w.write(", ");
797             }
798 
799             w.write(attribute.name());
800 
801             first = false;
802         }
803 
804         w.write(");\n");
805         w.out();
806         w.write("}\n");
807     }
808 
809     private void genTypes(IndentWriter w, List<ExternalizedTypeElement> types) throws IOException {
810         boolean first = true;
811         for (ExternalizedTypeElement type : types) {
812             if (!first) {
813                 w.write(", ");
814             }
815 
816             genType(w, type);
817 
818             first = false;
819         }
820     }
821 
822     private void genType(IndentWriter w, ExternalizedTypeElement type) throws IOException {
823         w.write("OnnxType." + replaceTypeIdentifier(type.identifier()));
824         w.write("(");
825         genTypes(w, type.arguments());
826         w.write(")");
827     }
828 
829     private String replaceTypeIdentifier(String i) {
830         return switch (i) {
831             case "float" -> "float32";
832             case "double" -> "float64";
833             default -> i;
834         };
835     }
836 
837     static ExternalizedTypeElement parseTypeString(String type_str) {
838         return ExternalizedTypeElement.ofString(
839                 type_str.replace('(', '<').replace(')', '>'));
840     }
841 
842     static Class<?> toBoxType(Class<?> pc) {
843         if (pc == byte.class) {
844             return Byte.class;
845         } else if (pc == short.class) {
846             return Short.class;
847         } else if (pc == int.class) {
848             return Integer.class;
849         } else if (pc == long.class) {
850             return Long.class;
851         } else if (pc == float.class) {
852             return Float.class;
853         } else if (pc == double.class) {
854             return Double.class;
855         } else if (pc == boolean.class) {
856             return Boolean.class;
857         } else {
858             return pc;
859         }
860     }
861 
862     public static void main(String[] args) throws Exception {
863         List<OpSchema> schemas = OpSchemaParser.parse(Path.of(
864                 "opgen/onnx-schema.json"));
865         OpGen opGen = new OpGen(schemas);
866 
867         opGen.genOpsClass(Path.of("src/main/java/oracle/code/onnx/ir"));
868     }
869 }