ljclang

FORK: A LuaJIT-based interface to libclang
git clone https://git.neptards.moe/neptards/ljclang.git
Log | Files | Refs

extractdecls.lua (7139B)


      1 #!/usr/bin/env luajit
      2 
      3 local arg = arg
      4 
      5 local string = require("string")
      6 local io = require("io")
      7 local os = require("os")
      8 local table = require("table")
      9 
     10 local cl = require("ljclang")
     11 
     12 local format = string.format
     13 local print = print
     14 
     15 ----------
     16 
     17 local function printf(fmt, ...)
     18     print(format(fmt, ...))
     19 end
     20 
     21 local function errprintf(fmt, ...)
     22     io.stderr:write(format(fmt, ...).."\n")
     23 end
     24 
     25 local function usage(hline)
     26     if (hline) then
     27         print(hline)
     28     end
     29     print("Usage: "..arg[0].." [our_options...] <file.h> [clang_options...]")
     30     print "  -p <filterPattern>"
     31     print "  -x <excludePattern1> [-x <excludePattern2>] ..."
     32     print "  -s <stripPattern>"
     33     print "  -1 <string to print before everything>"
     34     print "  -2 <string to print after everything>"
     35     print "  -C: print lines like"
     36     print "       static const int membname = 123;  (enums/macros only)"
     37     print "  -R: reverse mapping, only if one-to-one. Print lines like"
     38     print "       [123] = \"membname\";  (enums/macros only)"
     39     print "  -f <formatFunc>: user-provided body for formatting function (enums/macros only)"
     40     print "       Accepts args `k', `v'; `f' is string.format. Must return a formatted line."
     41     print[[       Example: "return f('%s = %s%s,', k, k:find('KEY_') and '65536+' or '', v)"]]
     42     print "       Incompatible with -C or -R."
     43     print "  -Q: be quiet"
     44     print "  -w: extract what? Can be"
     45     print "       EnumConstantDecl (default), TypedefDecl, FunctionDecl, MacroDefinition"
     46     os.exit(1)
     47 end
     48 
     49 local parsecmdline = require("parsecmdline_pk")
     50 
     51 -- Meta-information about options, see parsecmdline_pk.
     52 local opt_meta = { p=true, x=1, s=true, C=false, R=false, Q=false,
     53                    ['1']=true, ['2']=true, w=true, f=true }
     54 
     55 local opts, args = parsecmdline.getopts(opt_meta, arg, usage)
     56 
     57 local pat = opts.p
     58 local xpats = opts.x
     59 local spat = opts.s
     60 local constint = opts.C
     61 local reverse = opts.R
     62 local quiet = opts.Q
     63 local what = opts.w or "EnumConstantDecl"
     64 local fmtfuncCode = opts.f
     65 
     66 local extractEnum = (what == "EnumConstantDecl")
     67 local extractMacro = (what:find("^Macro"))
     68 
     69 local printbefore = opts['1']
     70 local printafter = opts['2']
     71 
     72 if (#args == 0) then
     73     usage()
     74 end
     75 
     76 if (not (extractEnum or extractMacro) and (constint or reverse)) then
     77     usage("Options -C and -R only available for enum or macro extraction")
     78 end
     79 
     80 local fmtfunc
     81 if (fmtfuncCode) then
     82     if (not (extractEnum or extractMacro)) then
     83         usage("Option -f only available for enum or macro extraction")
     84     end
     85 
     86     if (constint or reverse) then
     87         usage("Option -f is incompatible with -C or -R")
     88     end
     89 
     90     local func, errmsg = loadstring([[
     91 local f=string.format
     92 return function(k, v)
     93 ]]..fmtfuncCode..[[
     94 end
     95 ]])
     96     if (func == nil) then
     97         io.stderr:write("Error loading string: "..errmsg.."\n")
     98         os.exit(1)
     99     end
    100 
    101     fmtfunc = func()
    102 end
    103 
    104 local opts = extractMacro and {"DetailedPreprocessingRecord"} or nil
    105 
    106 local filename = args[1]
    107 do
    108     local f, msg = io.open(filename)
    109     if (f == nil) then
    110         errprintf("ERROR: Failed opening %s", msg)
    111         os.exit(1)
    112     end
    113     f:close()
    114 end
    115 
    116 local index = cl.createIndex(true, false)
    117 local tu = index:parse("", args, opts)
    118 if (tu == nil) then
    119     errprintf("ERROR: Failed parsing %s", filename)
    120     os.exit(1)
    121 end
    122 
    123 if (not quiet) then
    124     local diags = tu:diagnostics()
    125     for i=1,#diags do
    126         local d = diags[i]
    127         io.stderr:write(d.text.."\n")
    128     end
    129 end
    130 
    131 -- Mapping of enum value to its name for -R.
    132 local enumname = {}
    133 -- Mapping of running index to enum value for -R.
    134 local enumseq = {}
    135 
    136 local V = cl.ChildVisitResult
    137 
    138 local function checkexclude(name)
    139     for i=1,#xpats do
    140         if (name:find(xpats[i])) then
    141             return true
    142         end
    143     end
    144 end
    145 
    146 -- Get definition string of #define macro definition cursor.
    147 local function getDefStr(cur)
    148     local tokens = cur:_tokens()
    149     -- TOKENIZE_WORKAROUND
    150 --    print("tokens: ["..table.concat(tokens, "|", 2, #tokens).."]")
    151     if (#tokens >= 3) then
    152         tokens[#tokens] = nil
    153     end
    154     return table.concat(tokens, " ", 2, #tokens)
    155 end
    156 
    157 local visitor = cl.regCursorVisitor(
    158 function(cur, parent)
    159     if (extractEnum) then
    160         if (cur:haskind("EnumDecl")) then
    161             return V.Recurse
    162         end
    163     end
    164 
    165     if (cur:haskind(what)) then
    166         local name = cur:displayName()
    167 
    168         if (pat == nil or name:find(pat)) then
    169             local exclude = false
    170 
    171             if (not checkexclude(name)) then
    172                 local ourname = spat and name:gsub(spat, "") or name
    173 
    174                 if (extractEnum or extractMacro) then
    175                     -- Enumeration constants
    176                     local val = extractEnum and cur:enumval() or getDefStr(cur)
    177 
    178                     -- NOTE: tonumber(val) == nil can only happen with #defines that are not
    179                     -- like a literal number.
    180                     if (not extractMacro or tonumber(val) ~= nil) then
    181                         if (fmtfunc) then
    182                             print(fmtfunc(ourname, val))
    183                         elseif (reverse) then
    184                             if (enumname[val]) then
    185                                 printf("Error: enumeration value %d not unique: %s and %s",
    186                                        val, enumname[val], ourname)
    187                                 os.exit(2)
    188                             end
    189                             enumname[val] = ourname
    190                             enumseq[#enumseq+1] = val
    191                         elseif (constint) then
    192                             printf("static const int %s = %s;", ourname, val)
    193                         else
    194                             printf("%s = %s,", ourname, val)
    195                         end
    196                     end
    197                 elseif (what=="FunctionDecl") then
    198                     -- Function declaration
    199                     local rettype = cur:resultType()
    200                     if (not checkexclude(rettype:name())) then
    201                         printf("%s %s;", rettype, ourname)
    202                     end
    203                 elseif (what=="TypedefDecl") then
    204                     -- Type definition
    205                     local utype = cur:typedefType()
    206                     if (not checkexclude(utype:name())) then
    207                         printf("typedef %s %s;", utype, ourname)
    208                     end
    209 --[[
    210                 elseif (extractMacro) then
    211                     local fn, linebeg, lineend = cur:location(true)
    212                     local defstr = getDefStr(cur)
    213 
    214                     printf("%s @ %s:%d%s :: %s", ourname, fn, linebeg,
    215                            (lineend~=linebeg) and "--"..lineend or "", defstr)
    216 --]]
    217                 else
    218                     -- Anything else
    219                     printf("%s", ourname)
    220                 end
    221             end
    222         end
    223     end
    224 
    225     return V.Continue
    226 end)
    227 
    228 if (printbefore) then
    229     print(printbefore)
    230 end
    231 
    232 tu:cursor():children(visitor)
    233 
    234 if (reverse) then
    235     for i=1,#enumseq do
    236         local val = enumseq[i]
    237         local name = enumname[val]
    238         printf("[%d] = %q;", val, name)
    239     end
    240 end
    241 
    242 if (printafter) then
    243     print(printafter)
    244 end