#!/usr/bin/perl -w # # Copyright 2025 Henri Verbeet # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA use strict; use warnings; use JSON; use open ':utf8'; binmode STDOUT, ':utf8'; sub opcode_id($) { sprintf "0x%04x", shift; } sub enumerant_id($) { my ($value) = @_; sprintf "%#x", $value =~ /^0[xX]/ ? hex $value : $value; } sub fix_name($) { shift =~ s/([A-Z]+)/_$1/rg; } sub operand_category_name(_) { "SPIRV_PARSER_OPERAND_CATEGORY${\uc fix_name shift}"; } sub operand_type_name(_) { "SPIRV_PARSER_OPERAND_TYPE${\uc fix_name shift}"; } sub grammar_version($) { my ($grammar) = @_; "$grammar->{major_version}.$grammar->{minor_version}.$grammar->{revision}"; } sub print_enumerant(_) { my ($enumerant) = @_; my $indent = " " x 12; my $parameter_count = @{$enumerant->{parameters} // []}; if (!$parameter_count) { print $indent . "{${\enumerant_id $enumerant->{value}}, \"$enumerant->{enumerant}\"},\n"; return; } print $indent . "{\n"; print $indent . " ${\enumerant_id $enumerant->{value}}, \"$enumerant->{enumerant}\", $parameter_count,\n"; print $indent . " (enum spirv_parser_operand_type[])\n"; print $indent . " {\n"; print $indent . " ${\operand_type_name $_->{kind}},\n" foreach @{$enumerant->{parameters}}; print $indent . " }\n"; print $indent . "},\n"; } sub print_operand_type_info(_) { my ($type) = @_; my $enumerant_count = @{$type->{enumerants} // []}; print " [${\operand_type_name $type->{kind}}] =\n"; print " {\n"; print " \"$type->{kind}\", ${\operand_category_name $type->{category}}" . ($enumerant_count ? ", $enumerant_count,\n" : "\n"); if ($enumerant_count) { print " (struct spirv_parser_enumerant[])\n"; print " {\n"; print_enumerant foreach @{$type->{enumerants}}; print " }\n"; } print " },\n"; } sub instruction_operand(_) { my ($operand) = @_; "{${\operand_type_name $operand->{kind}}" . (defined $operand->{quantifier} ? ", '$operand->{quantifier}'}" : "}"); } sub print_opcode_info(_) { my ($instruction) = @_; my $operand_count = @{$instruction->{operands} // []}; if (!$operand_count) { print " {${\opcode_id $instruction->{opcode}}, \"$instruction->{opname}\"},\n"; return; } print " {\n"; print " ${\opcode_id $instruction->{opcode}}, \"$instruction->{opname}\", $operand_count,\n"; print " (struct spirv_parser_instruction_operand[])\n"; print " {\n"; print " ${\instruction_operand},\n" foreach @{$instruction->{operands}}; print " }\n"; print " },\n"; } sub print_header($) { my ($grammar) = @_; my @operand_types = sort {$a->{kind} cmp $b->{kind}} @{$grammar->{operand_kinds}}; print "/* This file is automatically generated from version ${\grammar_version $grammar} of the\n"; print " * machine-readable SPIR-V grammar.\n"; print " *\n"; print " * The original source is covered by the following license:\n"; print " *\n"; print map {" * $_" =~ s/ +$//r . "\n"} @{$grammar->{copyright}}; print " */\n\n"; print "enum spirv_parser_operand_category\n"; print "{\n"; print " ${\operand_category_name},\n" foreach sort keys %{{map {$_->{category}, undef} @operand_types}}; print "};\n\n"; print "enum spirv_parser_operand_type\n"; print "{\n"; print " ${\operand_type_name $_->{kind}},\n" foreach @operand_types; print "};\n\n"; print "static const struct spirv_parser_operand_type_info\n"; print "{\n"; print " const char *name;\n"; print " enum spirv_parser_operand_category category;\n"; print " size_t enumerant_count;\n"; print " const struct spirv_parser_enumerant\n"; print " {\n"; print " uint32_t value;\n"; print " const char *name;\n"; print " size_t parameter_count;\n"; print " enum spirv_parser_operand_type *parameters;\n"; print " } *enumerants;\n"; print "}\n"; print "spirv_parser_operand_type_info[] =\n"; print "{\n"; print_operand_type_info foreach @operand_types; print "};\n\n"; print "static const struct spirv_parser_opcode_info\n"; print "{\n"; print " uint16_t op;\n"; print " const char *name;\n"; print " size_t operand_count;\n"; print " const struct spirv_parser_instruction_operand\n"; print " {\n"; print " enum spirv_parser_operand_type type;\n"; print " char quantifier;\n"; print " } *operands;\n"; print "}\n"; print "spirv_parser_opcode_info[] =\n"; print "{\n"; print_opcode_info foreach sort {$a->{opcode} <=> $b->{opcode}} @{$grammar->{instructions}}; print "};\n"; } die "No input file specified.\n" unless @ARGV; print_header do { local $/; open my $fh, '<', $ARGV[0] or die $!; decode_json <$fh>; };