• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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