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.

186 lines
4.6 KiB
Ruby

# frozen_string_literal: true
require_relative 'util'
require 'set'
class Parser
def aliaser
@aliaser ||= Aliaser.new
end
end
class Aliaser
def initialize
@aliases = {} # str -> Regexp
end
def initialize_copy orig
@aliases = orig.instance_variable_get(:@aliases).dup
end
private def escape_alias_key str
Regexp.new "(?<=[^a-zA-Z:_])#{Regexp.escape str}(?=[^a-zA-Z0-9_])"
end
BUILTINS = Set.new %w(signed unsigned char short int long float double bool)
def add_alias type, alias_value
n = Util.remove_anon_ns(type.spelling).gsub(
/([^a-zA-Z:_])([a-zA-Z_][a-zA-Z0-9_]*)([^a-zA-Z0-9_])/) do
pre, typ, suf = $1, $2, $3
if BUILTINS.member? typ
"#{pre}#{typ}#{suf}"
else
"#{pre}::#{typ}#{suf}"
end
end
# p [:addalias, type.spelling, n, alias_value]
@aliases[n] = alias_value
@aliases["::#{n}"] = alias_value
end
# extremely hacky way to get fully qualified-ish names
# totally not optimal, but this is probably the best we can get without
# hacking even more clang internals or writing a real libtooling tool in C++...
#
# collect fully qualified names of the type and all template arguments and all
# base types
#
# the latter is required in case of templates:
# void foo(const typename Bar<T, Asd>::InnerStuff&)
# we need to turn Bar<...>::InnerStuff into Libshit::Bar...
# but we only get the InnerStuff type, it's path won't match the template hell
# calling canonical on it will resolve the typedef so it won't work
private def collect_ns out, t
return unless t
pt = t.pointee
# note: since clang 8.0, t can end up as `auto` and pointee is `auto` in
# that case, avoid infinite recursion
collect_ns out, pt if pt && t != pt
cur = t.declaration
return if cur.kind == :no_decl_found
t.template_arguments.each {|v| collect_ns out, v }
collect_ns_cur out, cur, 0
end
Item = Struct.new :path, :prio
private def collect_ns_cur out, cur, prio
path = [ cur.spelling ]
repl = [ cur.spelling ]
par = cur.semantic_parent
while !par.null? && par.kind != :translation_unit
collect_ns out, par.type
unless par.spelling.empty?
path.unshift par.spelling
repl.unshift par.display_name
end
par = par.semantic_parent
end
out['::'+repl.join('::')] = Item.new path, prio
end
# generate gsub patterns for collected paths
private def gen_gsub items
pats = {}
prios = {}
items.each do |k,v|
repl = Util.remove_anon_ns k
# generate fqns too (::Libshit::Foo::Bar)
path = [''] + v.path
until path.empty?
pat = escape_alias_key path.join '::'
if pats[pat] && pats[pat] != repl && prios[pat] == v.prio
Util.print_error "Ambiguous name?\nPattern: #{pat}\nOld repl: " \
"#{pats[pat]}\nNew repl: #{repl}"
elsif prios[pat].nil? || prios[pat] < v.prio
pats[pat] = repl
prios[pat] = v.prio
end
path.shift
end
end
pats
end
private def find_typedefs items, cur
return if cur.null?
cur.visit_children do |c, par|
collect_ns_cur items, c, 10 if c.kind == :type_alias_decl
:continue
end
find_typedefs items, cur.semantic_parent
end
private def apply_rules str, pats, verbose
ret = "##{Util.remove_anon_ns str}#"
chg = true
p [:start, ret] if verbose
while chg
chg = false
pats.each do |k,v|
p [:pat, k, v] if verbose
n = ret.gsub(k) { v }
if n != ret
p [:chg_to, n] if verbose
ret = n
chg = true
end
end
end
p [:end, ret] if verbose
ret[1..-2]
end
def type_name x, cur = nil
case x
when String; return x
when FFI::Clang::Cursor
t = x.type
cur = x
when FFI::Clang::Type
t = x
else
raise ArgumentError, 'Invalid type parameter'
end
items = {}
collect_ns items, t
find_typedefs items, cur if cur && !cur.null?
pats = gen_gsub items
tmppats = {}
@aliases.each do |k,v|
tmppats[escape_alias_key k] = v
tmppats[escape_alias_key apply_rules(k, pats, false)] = v
end
pats.merge! tmppats
verbose = false
apply_rules(t.spelling, pats, verbose).
gsub(/(?<=[^a-zA-Z:_])::std::__cxx11::/, '::std::').
gsub(/(?<=[^a-zA-Z:_])::std::__1::/, '::std::').freeze
end
def type_list args, wrap = nil, &blk
if wrap.is_a? String
xpre = "#{wrap}<"
wrap = ->x { "#{xpre}#{x}>" }
end
wrap ||= blk || ->x { x }
args.map {|x| wrap.call type_name x }.join ', '
end
end