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
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
|