require 'ostruct'
require 'delegate'
class Attritute < SimpleDelegator
def getter_name
r = name.capitalize
type == 'bool' ? 'Is' + r : 'Get' + r
end
def setter_name
'Set' + name.capitalize
end
def bool?
type == 'bool'
end
def enum?
type == 'enum'
end
def size?
type == 'size'
end
def multiple?
!bool? && multiple
end
def applicable_to?(item_type)
applicable_to.include?(item_type)
end
def set_flags?
(defined? flags) && flags.any? || enum? && values.any? { |v| v.flags && v.flags.any? }
end
end
module Metadata
module_function
def attributes
@data.attributes.map do |op|
Attritute.new(op)
end
end
def language
@data.language || ''
end
def extends_default?
!language.empty?
end
def item_types
['record', 'field', 'function', 'param']
end
def wrap_data(data)
@data = data
end
end
def Gen.on_require(data)
Metadata.wrap_data(data)
end
module MetadataGen
module_function
def attribute_name(attribute)
return attribute.name if Metadata.language.empty?
"#{Metadata.language.downcase}.#{attribute.name}"
end
def class_name(item_type)
item_type.capitalize + 'Metadata'
end
def validate_body(item_type, is_bool)
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool }
body = []
indent = ' ' * 4
attributes.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
unless a.multiple?
body << "#{indent * 2}if (HasAttribute(attribute)) {"
body << "#{indent * 3}return Error(\"Attribute '#{attribute_name(a)}' already defined\","
body << "#{indent * 3} Error::Type::MULTIPLE_ATTRIBUTE);"
body << "#{indent * 2}}"
end
if a.enum?
a.values.each do |v|
body << "#{indent * 2}if (value == \"#{v.value}\") {"
body << "#{indent * 3}return {};"
body << "#{indent * 2}}"
body << ""
end
body << "#{indent * 2}return Error(std::string(\"Attribute '#{attribute_name(a)}' have incorrect value '\").append(value) +"
body << "#{indent * 2} R\"('. Should be one of #{a.values.map(&:value)})\", Error::Type::INVALID_VALUE);"
elsif a.size?
body << "#{indent * 2}return ValidateSize(value);"
else
body << "#{indent * 2}return {};"
end
body << "#{indent}}"
body << ""
end
Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? != is_bool }.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
body << "#{indent * 2}return Error(\"Attribute '#{attribute_name(a)}' #{is_bool ? "must" : "must not"} have a value\","
body << "#{indent * 2} #{is_bool ? "Error::Type::MISSING_VALUE" : "Error::Type::UNEXPECTED_VALUE"});"
body << "#{indent}}"
body << ""
end
if Metadata::extends_default?
args = ['attribute']
args << 'value' unless is_bool
body << "#{indent}return pandasm::#{class_name(item_type)}::Validate(#{args.join(', ')});"
else
body << "#{indent}return Error(std::string(\"Unknown attribute '\").append(attribute) + \"'\","
body << "#{indent} Error::Type::UNKNOWN_ATTRIBUTE);"
end
body
end
def set_flags_body(item_type, is_bool)
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
body = []
indent = ' ' * 4
attributes.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
if defined? a.flags
body << "#{indent * 2}SetAccessFlags(GetAccessFlags() | #{a.flags.join(' | ')});"
end
if a.enum?
a.values.select { |v| v.flags && v.flags.any? }.each do |v|
body << "#{indent * 2}if (value == \"#{v.value}\") {"
body << "#{indent * 3}SetAccessFlags(GetAccessFlags() | #{v.flags.join(' | ')});"
body << "#{indent * 2}}"
body << ""
end
end
body << "#{indent}}"
end
if Metadata::extends_default?
args = ['attribute']
args << 'value' unless is_bool
body << "#{indent}pandasm::#{class_name(item_type)}::SetFlags(#{args.join(', ')});"
end
body
end
def remove_flags_body(item_type, is_bool)
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
body = []
indent = ' ' * 4
attributes.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
if defined? a.flags
body << "#{indent * 2}if ((GetAccessFlags() & #{a.flags.join(' | ')}) != 0) {"
body << "#{indent * 3}SetAccessFlags(GetAccessFlags() ^ (#{a.flags.join(' | ')}));"
body << "#{indent * 2}}"
end
if a.enum?
a.values.select { |v| v.flags && v.flags.any? }.each do |v|
body << "#{indent * 2}if (value == \"#{v.value}\") {"
body << "#{indent * 3}if ((GetAccessFlags() & (#{v.flags.join(' | ')})) != 0) {"
body << "#{indent * 4}SetAccessFlags(GetAccessFlags() ^ (#{v.flags.join(' | ')}));"
body << "#{indent * 3}}"
body << "#{indent * 2}}"
end
end
body << "#{indent}}"
end
if Metadata::extends_default?
args = ['attribute']
args << 'value' unless is_bool
body << "#{indent}pandasm::#{class_name(item_type)}::RemoveFlags(#{args.join(', ')});"
end
body
end
def arg_list(is_bool)
args = ['const std::string_view &attribute']
args << 'const std::string_view &value' if !is_bool
args
end
def add_unused_attribute(arg)
"[[maybe_unused]] #{arg}"
end
def validate_arg_list(item_type, is_bool)
args = arg_list(is_bool)
return args if Metadata::extends_default?
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool }
args[0] = add_unused_attribute(args[0]) if attributes.none?
args[1] = add_unused_attribute(args[1]) if args[1] && attributes.none? { |a| a.enum? }
args
end
def flags_arg_list(item_type, is_bool)
args = arg_list(is_bool)
return args if Metadata::extends_default?
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
use_value = attributes.any? { |a| a.enum? && a.values.any? { |v| v.flags && v.flags.any? } }
args[0] = add_unused_attribute(args[0]) if attributes.none?
args[1] = add_unused_attribute(args[1]) if args[1] && !use_value
args
end
end