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

# 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