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