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 Attritute < SimpleDelegator
18  def getter_name
19    r = name.capitalize
20    type == 'bool' ? 'Is' + r : 'Get' + r
21  end
22
23  def setter_name
24    'Set' + name.capitalize
25  end
26
27  def bool?
28    type == 'bool'
29  end
30
31  def enum?
32    type == 'enum'
33  end
34
35  def size?
36    type == 'size'
37  end
38
39  def multiple?
40    !bool? && multiple
41  end
42
43  def applicable_to?(item_type)
44    applicable_to.include?(item_type)
45  end
46
47  def set_flags?
48    (defined? flags) && flags.any? || enum? && values.any? { |v| v.flags && v.flags.any? }
49  end
50end
51
52module Metadata
53  module_function
54
55  def attributes
56    @data.attributes.map do |op|
57      Attritute.new(op)
58    end
59  end
60
61  def language
62    @data.language || ''
63  end
64
65  def extends_default?
66    !language.empty?
67  end
68
69  def item_types
70    ['record', 'field', 'function', 'param']
71  end
72
73  def wrap_data(data)
74    @data = data
75  end
76end
77
78def Gen.on_require(data)
79  Metadata.wrap_data(data)
80end
81
82module MetadataGen
83  module_function
84
85  def attribute_name(attribute)
86    return attribute.name if Metadata.language.empty?
87    "#{Metadata.language.downcase}.#{attribute.name}"
88  end
89
90  def class_name(item_type)
91    item_type.capitalize + 'Metadata'
92  end
93
94  def validate_body(item_type, is_bool)
95    attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool }
96    body = []
97    indent = ' ' * 4
98
99    attributes.each do |a|
100      body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
101
102      unless a.multiple?
103        body << "#{indent * 2}if (HasAttribute(attribute)) {"
104        body << "#{indent * 3}return Error(\"Attribute '#{attribute_name(a)}' already defined\","
105        body << "#{indent * 3}             Error::Type::MULTIPLE_ATTRIBUTE);"
106        body << "#{indent * 2}}"
107      end
108
109      if a.enum?
110        a.values.each do |v|
111          body << "#{indent * 2}if (value == \"#{v.value}\") {"
112          body << "#{indent * 3}return {};"
113          body << "#{indent * 2}}"
114          body << ""
115        end
116
117        body << "#{indent * 2}return Error(std::string(\"Attribute '#{attribute_name(a)}' have incorrect value '\").append(value) +"
118        body << "#{indent * 2}             R\"('. Should be one of #{a.values.map(&:value)})\", Error::Type::INVALID_VALUE);"
119      elsif a.size?
120        body << "#{indent * 2}return ValidateSize(value);"
121      else
122        body << "#{indent * 2}return {};"
123      end
124
125      body << "#{indent}}"
126      body << ""
127    end
128
129    Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? != is_bool }.each do |a|
130      body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
131      body << "#{indent * 2}return Error(\"Attribute '#{attribute_name(a)}' #{is_bool ? "must" : "must not"} have a value\","
132      body << "#{indent * 2}             #{is_bool ? "Error::Type::MISSING_VALUE" : "Error::Type::UNEXPECTED_VALUE"});"
133      body << "#{indent}}"
134      body << ""
135    end
136
137    if Metadata::extends_default?
138      args = ['attribute']
139      args << 'value' unless is_bool
140      body << "#{indent}return pandasm::#{class_name(item_type)}::Validate(#{args.join(', ')});"
141    else
142      body << "#{indent}return Error(std::string(\"Unknown attribute '\").append(attribute) + \"'\","
143      body << "#{indent}             Error::Type::UNKNOWN_ATTRIBUTE);"
144    end
145
146    body
147  end
148
149  def set_flags_body(item_type, is_bool)
150    attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
151    body = []
152    indent = ' ' * 4
153
154    attributes.each do |a|
155      body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
156
157      if defined? a.flags
158        body << "#{indent * 2}SetAccessFlags(GetAccessFlags() | #{a.flags.join(' | ')});"
159      end
160
161      if a.enum?
162        a.values.select { |v| v.flags && v.flags.any? }.each do |v|
163          body << "#{indent * 2}if (value == \"#{v.value}\") {"
164          body << "#{indent * 3}SetAccessFlags(GetAccessFlags() | #{v.flags.join(' | ')});"
165          body << "#{indent * 2}}"
166          body << ""
167        end
168      end
169
170      body << "#{indent}}"
171    end
172
173    if Metadata::extends_default?
174        args = ['attribute']
175        args << 'value' unless is_bool
176        body << "#{indent}pandasm::#{class_name(item_type)}::SetFlags(#{args.join(', ')});"
177    end
178
179    body
180  end
181
182  def remove_flags_body(item_type, is_bool)
183    attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
184    body = []
185    indent = ' ' * 4
186
187    attributes.each do |a|
188      body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
189
190      if defined? a.flags
191        body << "#{indent * 2}if ((GetAccessFlags() & #{a.flags.join(' | ')}) != 0) {"
192        body << "#{indent * 3}SetAccessFlags(GetAccessFlags() ^ (#{a.flags.join(' | ')}));"
193        body << "#{indent * 2}}"
194      end
195
196      if a.enum?
197        a.values.select { |v| v.flags && v.flags.any? }.each do |v|
198          body << "#{indent * 2}if (value == \"#{v.value}\") {"
199          body << "#{indent * 3}if ((GetAccessFlags() & (#{v.flags.join(' | ')})) != 0) {"
200          body << "#{indent * 4}SetAccessFlags(GetAccessFlags() ^ (#{v.flags.join(' | ')}));"
201          body << "#{indent * 3}}"
202          body << "#{indent * 2}}"
203        end
204      end
205
206      body << "#{indent}}"
207    end
208
209    if Metadata::extends_default?
210      args = ['attribute']
211      args << 'value' unless is_bool
212      body << "#{indent}pandasm::#{class_name(item_type)}::RemoveFlags(#{args.join(', ')});"
213    end
214
215    body
216  end
217
218  def arg_list(is_bool)
219    args = ['std::string_view attribute']
220    args << 'std::string_view value' if !is_bool
221    args
222  end
223
224  def add_unused_attribute(arg)
225    "[[maybe_unused]] #{arg}"
226  end
227
228  def validate_arg_list(item_type, is_bool)
229    args = arg_list(is_bool)
230    return args if Metadata::extends_default?
231
232    attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool }
233    args[0] = add_unused_attribute(args[0]) if attributes.none?
234    args[1] = add_unused_attribute(args[1]) if args[1] && attributes.none? { |a| a.enum? }
235    args
236  end
237
238  def flags_arg_list(item_type, is_bool)
239    args = arg_list(is_bool)
240    return args if Metadata::extends_default?
241
242    attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
243    use_value = attributes.any? { |a| a.enum? && a.values.any? { |v| v.flags && v.flags.any? } }
244    args[0] = add_unused_attribute(args[0]) if attributes.none?
245    args[1] = add_unused_attribute(args[1]) if args[1] && !use_value
246    args
247  end
248
249end
250