libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

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