1# Copyright (c) 2021-2024 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14require 'ostruct' 15require 'delegate' 16 17class String 18 def snakecase 19 gsub(/::/, '/') 20 .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') 21 .gsub(/([a-z\d])([A-Z])/, '\1_\2') 22 .tr('-', '_') 23 .downcase 24 end 25end 26 27class OpenStruct 28 # Versions prior to 2.7.1 lack of `to_h` overload, so define something similar: 29 def to_stringhash 30 h = {} 31 each_pair { |k, v| h[k.to_s] = v } 32 h 33 end 34end 35 36class Option < SimpleDelegator 37 def initialize(data) 38 super 39 if has_sub_options? 40 raise "Compound option should not have `type` field, it is always bool" if respond_to?(:type) 41 raise "Compound option should not have `default` field, it is always `false``" if respond_to?(:default) 42 self[:type] = 'bool' 43 self[:default] = 'false' 44 end 45 end 46 47 def raise(msg) 48 super "in '#{name}': #{msg}" 49 end 50 51 def fix(options) 52 if has_sub_options? 53 sub_options.map { |subopt| subopt.fix(options) } 54 return 55 end 56 fix_references(options) 57 flatten_lists_of_option_values 58 verify 59 end 60 61 def fix_references(options) 62 if respond_to?(:possible_values) && possible_values.is_a?(String) 63 raise "Wrong reference syntax: #{possible_values}" if possible_values[0] != '$' 64 p = possible_values[1..-1] 65 target = options.find {|x| x.name == p } 66 raise "Invalid reference #{possible_values}" if target.nil? 67 self["possible_values"] = target["possible_values"] 68 end 69 end 70 71 def flatten_lists_of_option_values 72 if self[:possible_values].is_a? OpenStruct 73 self[:possible_values] = possible_values.to_stringhash.flatten(2) 74 end 75 end 76 77 def verify 78 if respond_to? :default 79 supported_default_types = [String, Array, TrueClass, FalseClass, Integer, Float] 80 if not supported_default_types.member? self[:default].class 81 raise "Syntax error, 'default' should be of #{supported_default_types} type, got '#{self[:default].class}'" 82 elsif (self[:default].is_a? Array) != (type == 'arg_list_t') 83 raise "Syntax error, 'Array'-default should be used with 'arg_list_t'-options" 84 end 85 end 86 end 87 88 def field_name 89 name.tr('-', '_').tr('.', '_') + '_' 90 end 91 92 def camel_name 93 n = name.split(Regexp.union(['-', '.'])).map(&:capitalize).join 94 sub_option? ? (self.parent.camel_name + n) : n 95 end 96 97 def view_type 98 case type 99 when 'std::string' 100 'const std::string &' 101 when 'arg_list_t' 102 'const arg_list_t &' 103 else 104 type 105 end 106 end 107 108 def getter_signature 109 ret_type = view_type 110 ret_type += ' ' if ret_type[-1] != '&' 111 ret_type + getter_name + '([[maybe_unused]] std::string_view lang = "") const' 112 end 113 114 def getter_name 115 (type == 'bool' ? 'Is' : 'Get') + camel_name 116 end 117 118 def setter_name 119 'Set' + camel_name 120 end 121 122 def sub_option? 123 respond_to?(:parent) 124 end 125 126 def has_sub_options? 127 respond_to?(:sub_options) 128 end 129 130 def has_enum? 131 respond_to?(:enum) 132 end 133 134 def flat_enum 135 raise "'enum' has invalid type" if !enum.is_a?(Array) && !enum.is_a?(OpenStruct) 136 return enum if enum.is_a?(Array) 137 enum.to_h.values.flatten 138 end 139 140 def sub_enums 141 return {} if enum.is_a?(Array) 142 enum.to_stringhash 143 end 144 145 def deprecated? 146 respond_to?(:deprecated) && deprecated 147 end 148 149 def lang_specific? 150 respond_to?(:lang_specific) 151 end 152 153 def has_lang_suboptions? 154 respond_to?(:lang) 155 end 156 157 def lang_field_name(lang) 158 "#{lang}.#{name}".tr('-', '_').tr('.', '_') + '_' 159 end 160 161 def lang_camel_name(lang) 162 n = "#{lang}.#{name}".split(Regexp.union(['-', '.'])).map(&:capitalize).join 163 sub_option? ? (self.parent.camel_name + n) : n 164 end 165 166 def default_value 167 if default_target_specific? 168 raise "Syntax error, 'default_target_specific' should be of 'OpenStruct' class, got '#{default_target_specific.class}'" unless default_target_specific.is_a?(OpenStruct) 169 res = "ark::default_target_options::#{getter_name }({" 170 default_target_specific.each_pair { |k, v| res += "{#{expand_string k.to_s}, #{v}}," } 171 res += "})" 172 else 173 return default_constant_name if need_default_constant 174 return '{' + default.map { |e| expand_string(e) }.join(', ') + '}' if type == 'arg_list_t' 175 return expand_string(default) if type == 'std::string' 176 default 177 end 178 end 179 180 def full_description 181 full_desc = description 182 full_desc.prepend("[DEPRECATED] ") if deprecated? 183 if defined? possible_values 184 full_desc += "." unless full_desc[-1] == "." 185 full_desc += ' Possible values: ' + possible_values.inspect 186 end 187 full_desc += "." unless full_desc[-1] == "." 188 if default_target_specific? 189 full_desc += " Default (target specific):\n" 190 default_target_specific.each_pair{ |k, v| full_desc += " on '#{k.to_s}': #{v}\n" } 191 else 192 full_desc += ' Default: ' + default.inspect 193 end 194 Common::to_raw(full_desc) 195 end 196 197 def expand_string(s) 198 @expansion_map ||= { 199 '$ORIGIN' => 'exe_dir_' 200 } 201 @expansion_map.each do |k, v| 202 ret = "" 203 if s.include?(k) 204 split = s.split(k); 205 for i in 1..split.length() - 1 206 ret += v + ' + ' + Common::to_raw(split[i])+ ' + ' 207 end 208 return ret.delete_suffix(' + ') 209 end 210 end 211 Common::to_raw(s) 212 end 213 214 def default_target_specific? 215 respond_to? :default_target_specific 216 end 217 218 def need_default_constant 219 (type == 'int' || type == 'double' || type == 'uint32_t' || type == 'uint64_t') && !default_target_specific? 220 end 221 222 def default_constant_name 223 name.tr('-', '_').tr('.', '_').upcase + '_DEFAULT' 224 end 225end 226 227class Event < SimpleDelegator 228 def method_name 229 'Event' + name.split('-').map(&:capitalize).join 230 end 231 232 def args_list 233 args = '' 234 delim = '' 235 fields.each do |field| 236 args.concat(delim, field.type, ' ', field.name) 237 delim = ', ' 238 end 239 return args 240 end 241 242 def print_line 243 qoute = '"' 244 line = 'events_file_' 245 delim = ' << ' 246 fields.each do |field| 247 line.concat(delim, qoute, field.name, qoute) 248 delim = ' << ":" << ' 249 line.concat(delim, field.name) 250 delim = ' << "," << ' 251 end 252 return line 253 end 254 255end 256 257module Common 258 module_function 259 260 def create_sub_options(option, options) 261 return unless option.has_sub_options? 262 raise "Only boolean option can have sub options: #{option.name}" unless option.type == 'bool' 263 sub_options = [] 264 option.sub_options.each_with_object(sub_options) do |sub_option, sub_options| 265 sub_option[:parent] = option 266 sub_options << Option.new(sub_option) 267 end 268 options.concat sub_options 269 option[:sub_options] = sub_options 270 end 271 272 def options 273 @options ||= @data.options.each_with_object([]) do |option, options| 274 option_hash = option.to_h 275 new_option = nil 276 if option_hash.include?(:lang) 277 lang_spec_option = option_hash.clone 278 lang_spec_option.delete(:lang) 279 lang_spec_option[:lang_specific] = true 280 option.lang.each do |lang| 281 lang_spec_option[:description] = "#{option.description}. Only for #{lang}" 282 lang_spec_option[:name] = "#{lang}.#{option.name}" 283 new_option = Option.new(OpenStruct.new(lang_spec_option)) 284 create_sub_options(new_option, options) 285 options << new_option 286 end 287 main_option = option_hash.clone 288 main_option[:name] = "#{option.name}" 289 new_option = Option.new(OpenStruct.new(main_option)) 290 else 291 new_option = Option.new(option) 292 end 293 create_sub_options(new_option, options) 294 options << new_option 295 options.last.fix(@data.options) 296 end 297 @options 298 end 299 300 def events 301 @data.events.map do |op| 302 Event.new(op) 303 end 304 end 305 306 def module 307 @data.module 308 end 309 310 def to_raw(s) 311 'R"(' + s + ')"' 312 end 313 314 def wrap_data(data) 315 @data = data 316 end 317end 318 319def Gen.on_require(data) 320 Common.wrap_data(data) 321end 322