aliaser.rb (4715B)
1 # frozen_string_literal: true 2 3 require_relative 'util' 4 5 require 'set' 6 7 class Parser 8 def aliaser 9 @aliaser ||= Aliaser.new 10 end 11 end 12 13 class Aliaser 14 def initialize 15 @aliases = {} # str -> Regexp 16 end 17 18 def initialize_copy orig 19 @aliases = orig.instance_variable_get(:@aliases).dup 20 end 21 22 private def escape_alias_key str 23 Regexp.new "(?<=[^a-zA-Z:_])#{Regexp.escape str}(?=[^a-zA-Z0-9_])" 24 end 25 26 BUILTINS = Set.new %w(signed unsigned char short int long float double bool) 27 def add_alias type, alias_value 28 n = Util.remove_anon_ns(type.spelling).gsub( 29 /([^a-zA-Z:_])([a-zA-Z_][a-zA-Z0-9_]*)([^a-zA-Z0-9_])/) do 30 pre, typ, suf = $1, $2, $3 31 if BUILTINS.member? typ 32 "#{pre}#{typ}#{suf}" 33 else 34 "#{pre}::#{typ}#{suf}" 35 end 36 end 37 38 # p [:addalias, type.spelling, n, alias_value] 39 @aliases[n] = alias_value 40 @aliases["::#{n}"] = alias_value 41 end 42 43 # extremely hacky way to get fully qualified-ish names 44 # totally not optimal, but this is probably the best we can get without 45 # hacking even more clang internals or writing a real libtooling tool in C++... 46 # 47 # collect fully qualified names of the type and all template arguments and all 48 # base types 49 # 50 # the latter is required in case of templates: 51 # void foo(const typename Bar<T, Asd>::InnerStuff&) 52 # we need to turn Bar<...>::InnerStuff into Libshit::Bar... 53 # but we only get the InnerStuff type, it's path won't match the template hell 54 # calling canonical on it will resolve the typedef so it won't work 55 private def collect_ns out, t 56 return unless t 57 pt = t.pointee 58 # note: since clang 8.0, t can end up as `auto` and pointee is `auto` in 59 # that case, avoid infinite recursion 60 collect_ns out, pt if pt && t != pt 61 62 cur = t.declaration 63 return if cur.kind == :no_decl_found 64 65 t.template_arguments.each {|v| collect_ns out, v } 66 collect_ns_cur out, cur, 0 67 end 68 69 Item = Struct.new :path, :prio 70 private def collect_ns_cur out, cur, prio 71 path = [ cur.spelling ] 72 repl = [ cur.spelling ] 73 par = cur.semantic_parent 74 while !par.null? && par.kind != :translation_unit 75 collect_ns out, par.type 76 unless par.spelling.empty? 77 path.unshift par.spelling 78 repl.unshift par.display_name 79 end 80 par = par.semantic_parent 81 end 82 83 out['::'+repl.join('::')] = Item.new path, prio 84 end 85 86 # generate gsub patterns for collected paths 87 private def gen_gsub items 88 pats = {} 89 prios = {} 90 91 items.each do |k,v| 92 repl = Util.remove_anon_ns k 93 94 # generate fqns too (::Libshit::Foo::Bar) 95 path = [''] + v.path 96 until path.empty? 97 pat = escape_alias_key path.join '::' 98 if pats[pat] && pats[pat] != repl && prios[pat] == v.prio 99 Util.print_error "Ambiguous name?\nPattern: #{pat}\nOld repl: " \ 100 "#{pats[pat]}\nNew repl: #{repl}" 101 elsif prios[pat].nil? || prios[pat] < v.prio 102 pats[pat] = repl 103 prios[pat] = v.prio 104 end 105 106 path.shift 107 end 108 end 109 110 pats 111 end 112 113 private def find_typedefs items, cur 114 return if cur.null? 115 cur.visit_children do |c, par| 116 collect_ns_cur items, c, 10 if c.kind == :type_alias_decl 117 :continue 118 end 119 find_typedefs items, cur.semantic_parent 120 end 121 122 private def apply_rules str, pats, verbose 123 ret = "##{Util.remove_anon_ns str}#" 124 chg = true 125 p [:start, ret] if verbose 126 127 while chg 128 chg = false 129 pats.each do |k,v| 130 p [:pat, k, v] if verbose 131 n = ret.gsub(k) { v } 132 if n != ret 133 p [:chg_to, n] if verbose 134 ret = n 135 chg = true 136 end 137 end 138 end 139 140 p [:end, ret] if verbose 141 ret[1..-2] 142 end 143 144 def type_name x, cur = nil 145 case x 146 when String; return x 147 when FFI::Clang::Cursor 148 t = x.type 149 cur = x 150 151 when FFI::Clang::Type 152 t = x 153 154 else 155 raise ArgumentError, 'Invalid type parameter' 156 end 157 158 items = {} 159 collect_ns items, t 160 find_typedefs items, cur if cur && !cur.null? 161 pats = gen_gsub items 162 163 tmppats = {} 164 @aliases.each do |k,v| 165 tmppats[escape_alias_key k] = v 166 tmppats[escape_alias_key apply_rules(k, pats, false)] = v 167 end 168 pats.merge! tmppats 169 170 verbose = false 171 apply_rules(t.spelling, pats, verbose). 172 gsub(/(?<=[^a-zA-Z:_])::std::__cxx11::/, '::std::'). 173 gsub(/(?<=[^a-zA-Z:_])::std::__1::/, '::std::').freeze 174 end 175 176 def type_list args, wrap = nil, &blk 177 if wrap.is_a? String 178 xpre = "#{wrap}<" 179 wrap = ->x { "#{xpre}#{x}>" } 180 end 181 wrap ||= blk || ->x { x } 182 183 args.map {|x| wrap.call type_name x }.join ', ' 184 end 185 end