You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
4.4 KiB
Ruby
183 lines
4.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'set'
|
|
|
|
module Util
|
|
extend self
|
|
|
|
def print_fancy fun, str, c
|
|
to_print = String.new
|
|
if c
|
|
loc = c.location
|
|
to_print = "#{loc.file}:#{loc.line}:#{c.spelling}: "
|
|
end
|
|
to_print << (str.respond_to?(:full_message) ? str.full_message : str)
|
|
CI.send fun, to_print
|
|
end
|
|
|
|
@success = true
|
|
def success? = @success
|
|
def fail!; @success = false; end
|
|
def print_error str, c = nil
|
|
@success = false
|
|
print_fancy :eerror, str, c
|
|
end
|
|
|
|
def print_warning str, c = nil
|
|
print_fancy :ewarn, str, c
|
|
end
|
|
|
|
def remove_anon_ns str
|
|
str.gsub '::(anonymous namespace)', ''
|
|
end
|
|
|
|
def fun_qualified_name c
|
|
res = [ c.spelling ]
|
|
x = c.semantic_parent
|
|
while x&.kind == :namespace
|
|
res << x.spelling
|
|
x = x.semantic_parent
|
|
end
|
|
res.reverse.join '::'
|
|
end
|
|
|
|
# get template types
|
|
TEMPLATE_TYPES = Set.new %i(
|
|
template_type_parameter non_type_template_parameter
|
|
template_template_parameter
|
|
)
|
|
IGNORE_TYPES = Set.new %i(
|
|
parm_decl type_ref annotate_attr namespace_ref compound_stmt
|
|
)
|
|
def collect_template_args c
|
|
res = []
|
|
c.visit_children do |c, par|
|
|
kind = c.kind
|
|
if TEMPLATE_TYPES.member? kind
|
|
res << c
|
|
elsif !IGNORE_TYPES.member? kind
|
|
Util.print_warning "Unhandled arg type #{kind}", c
|
|
end
|
|
:continue
|
|
end
|
|
res
|
|
end
|
|
|
|
def collect_args c
|
|
res = []
|
|
c.visit_children do |c, par|
|
|
res << c if c.kind == :parm_decl
|
|
:continue
|
|
end
|
|
res
|
|
end
|
|
|
|
def has_annotation? c
|
|
if block_given?
|
|
c.visit_children do |c2|
|
|
return true if c2.kind == :annotate_attr && yield(c2.spelling)
|
|
:continue
|
|
end
|
|
else
|
|
c.visit_children do |c2|
|
|
return true if c2.kind == :annotate_attr
|
|
:continue
|
|
end
|
|
end
|
|
false
|
|
end
|
|
|
|
def collect_annotations c
|
|
res = []
|
|
if block_given?
|
|
c.visit_children do |c2|
|
|
if c2.kind == :annotate_attr && (mapped = yield(c2.spelling))
|
|
res << mapped
|
|
end
|
|
:continue
|
|
end
|
|
else
|
|
c.visit_children do |c2|
|
|
res << c2 if c2.kind == :annotate_attr
|
|
:continue
|
|
end
|
|
end
|
|
res
|
|
end
|
|
|
|
def map_annotation cls, key, ann, env = {}
|
|
return unless ann =~ /\A#{key}{(.*)}\z/
|
|
# binding fuckery: simply calling instance_eval from this function would
|
|
# mean that the parameters and the local variables here would end up in the
|
|
# scope of the annotation value eval
|
|
b = TOPLEVEL_BINDING.dup
|
|
b.local_variable_set '__eval_obj__', OpenStruct.new(env)
|
|
b.local_variable_set '__eval_cls__', cls
|
|
b.local_variable_set '__eval_str__', "__eval_cls__.new(#$1).freeze"
|
|
b.eval '__eval_obj__.instance_eval __eval_str__', __FILE__, __LINE__
|
|
end
|
|
|
|
CPP_ESCAPES = {
|
|
'\\' => '\\\\',
|
|
"\a" => '\\a',
|
|
"\b" => '\\b',
|
|
"\t" => '\\t',
|
|
"\n" => '\\n',
|
|
"\v" => '\\v',
|
|
"\f" => '\\f',
|
|
"\r" => '\\r',
|
|
}.transform_keys!(&:ord).freeze
|
|
def cpp_inspect s
|
|
return s.to_s unless s.is_a? String
|
|
esc = s.each_byte.map do |b|
|
|
next CPP_ESCAPES[b] if CPP_ESCAPES[b]
|
|
next b.chr if (32..127) === b
|
|
# octal is max 3 chars, hex can be any length, so use octal
|
|
'\\%03o' % b
|
|
end.join.gsub /(\\x[0-9a-f]{2})([0-9a-f])/, '\1""\2'
|
|
"\"#{esc}\""
|
|
end
|
|
end
|
|
|
|
module RecursiveClassDiscovery
|
|
def class_migrate_inheritable mod, ref; end
|
|
def get_class_info c, ann; ann; end
|
|
|
|
def class? type
|
|
@rec_class_cache ||= {}
|
|
type = type.canonical
|
|
name = type.spelling
|
|
return @rec_class_cache[name] unless @rec_class_cache[name].nil?
|
|
|
|
ann_cls = self.class.const_get :ClassAnnotation
|
|
ann_key = self.class.const_get :CLASS_ANNOTATION_KEY
|
|
ret = nil
|
|
tmp = nil
|
|
type.declaration.visit_children do |c, par|
|
|
case c.kind
|
|
when :cxx_base_specifier
|
|
if base_class = class?(c.type)
|
|
ret = true if ret.nil?
|
|
tmp ||= base_class.dup
|
|
class_migrate_inheritable tmp, base_class
|
|
end
|
|
|
|
when :annotate_attr
|
|
if at = Util.map_annotation(ann_cls, ann_key, c.spelling, c:)
|
|
ret = get_class_info par, at
|
|
end
|
|
end
|
|
:continue
|
|
end
|
|
|
|
ret = get_class_info type.declaration, ann_cls.new if ret == true
|
|
class_migrate_inheritable ret, tmp if tmp
|
|
@rec_class_cache[name] = ret
|
|
ret
|
|
end
|
|
|
|
def class_by_name name
|
|
@rec_class_cache[name] or fail "Unknown class #{name.inspect}"
|
|
end
|
|
end
|