neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

gen_declarations.lua (5203B)


      1 local grammar = require('gen.c_grammar').grammar
      2 
      3 --- @param fname string
      4 --- @return string?
      5 local function read_file(fname)
      6  local f = io.open(fname, 'r')
      7  if not f then
      8    return
      9  end
     10  local contents = f:read('*a')
     11  f:close()
     12  return contents
     13 end
     14 
     15 --- @param fname string
     16 --- @param contents string[]
     17 local function write_file(fname, contents)
     18  local contents_s = table.concat(contents, '\n') .. '\n'
     19  local fcontents = read_file(fname)
     20  if fcontents == contents_s then
     21    return
     22  end
     23  local f = assert(io.open(fname, 'w'))
     24  f:write(contents_s)
     25  f:close()
     26 end
     27 
     28 --- @param fname string
     29 --- @param non_static_fname string
     30 --- @return string? non_static
     31 local function add_iwyu_non_static(fname, non_static_fname)
     32  if fname:find('.*/src/nvim/.*%.c$') then
     33    -- Add an IWYU pragma comment if the corresponding .h file exists.
     34    local header_fname = fname:sub(1, -3) .. '.h'
     35    local header_f = io.open(header_fname, 'r')
     36    if header_f then
     37      header_f:close()
     38      return (header_fname:gsub('.*/src/nvim/', 'nvim/'))
     39    end
     40  elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then
     41    return 'nvim/api/private/dispatch.h'
     42  elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then
     43    return 'nvim/ui.h'
     44  elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then
     45    return 'nvim/ui_client.h'
     46  elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then
     47    return 'nvim/api/ui.h'
     48  end
     49 end
     50 
     51 --- @param d string
     52 local function process_decl(d)
     53  -- Comments are really handled by preprocessor, so the following is not
     54  -- needed
     55  d = d:gsub('/%*.-%*/', '')
     56  d = d:gsub('//.-\n', '\n')
     57  d = d:gsub('# .-\n', '')
     58  d = d:gsub('\n', ' ')
     59  d = d:gsub('%s+', ' ')
     60  d = d:gsub(' ?%( ?', '(')
     61  d = d:gsub(' ?, ?', ', ')
     62  d = d:gsub(' ?(%*+) ?', ' %1')
     63  d = d:gsub(' ?(FUNC_ATTR_)', ' %1')
     64  d = d:gsub(' $', '')
     65  d = d:gsub('^ ', '')
     66  return d .. ';'
     67 end
     68 
     69 --- @param fname string
     70 --- @param text string
     71 --- @return string[] static
     72 --- @return string[] non_static
     73 --- @return boolean any_static
     74 local function gen_declarations(text)
     75  local non_static = {} --- @type string[]
     76  local static = {} --- @type string[]
     77 
     78  local any_static = false
     79  for _, node in ipairs(grammar:match(text)) do
     80    if node[1] == 'proto' then
     81      local node_text = text:sub(node.pos, node.endpos - 1)
     82      local declaration = process_decl(node_text)
     83 
     84      if node.static then
     85        if not any_static and declaration:find('FUNC_ATTR_') then
     86          any_static = true
     87        end
     88        static[#static + 1] = declaration
     89      else
     90        non_static[#non_static + 1] = 'DLLEXPORT ' .. declaration
     91      end
     92    end
     93  end
     94 
     95  return static, non_static, any_static
     96 end
     97 
     98 local usage = [[
     99 Usage:
    100 
    101    gen_declarations.lua definitions.c static.h non-static.h
    102 
    103 Generates declarations for a C file definitions.c, putting declarations for
    104 static functions into static.h and declarations for non-static functions into
    105 non-static.h. Also generate an IWYU comment.
    106 ]]
    107 
    108 local function main()
    109  local fname = arg[1]
    110  local static_fname = arg[2]
    111  local non_static_fname = arg[3]
    112  local static_basename = arg[4]
    113 
    114  if fname == '--help' or #arg < 4 then
    115    print(usage)
    116    os.exit()
    117  end
    118 
    119  local text = assert(read_file(fname))
    120 
    121  local static_decls, non_static_decls, any_static = gen_declarations(text)
    122 
    123  local static = {} --- @type string[]
    124  if fname:find('.*/src/nvim/.*%.h$') then
    125    static[#static + 1] = ('// IWYU pragma: private, include "%s"'):format(
    126      fname:gsub('.*/src/nvim/', 'nvim/')
    127    )
    128  end
    129  vim.list_extend(static, {
    130    '#define DEFINE_FUNC_ATTRIBUTES',
    131    '#include "nvim/func_attr.h"',
    132    '#undef DEFINE_FUNC_ATTRIBUTES',
    133  })
    134  vim.list_extend(static, static_decls)
    135  vim.list_extend(static, {
    136    '#define DEFINE_EMPTY_ATTRIBUTES',
    137    '#include "nvim/func_attr.h"  // IWYU pragma: export',
    138    '',
    139  })
    140 
    141  write_file(static_fname, static)
    142 
    143  if any_static then
    144    local orig_text = assert(read_file(fname))
    145    local pat = '\n#%s?include%s+"' .. static_basename .. '"\n'
    146    local pat_comment = '\n#%s?include%s+"' .. static_basename .. '"%s*//'
    147    if not orig_text:find(pat) and not orig_text:find(pat_comment) then
    148      error(('fail: missing include for %s in %s'):format(static_basename, fname))
    149    end
    150  end
    151 
    152  if non_static_fname ~= 'SKIP' then
    153    local non_static = {} --- @type string[]
    154    local iwyu_non_static = add_iwyu_non_static(fname, non_static_fname)
    155    if iwyu_non_static then
    156      non_static[#non_static + 1] = ('// IWYU pragma: private, include "%s"'):format(
    157        iwyu_non_static
    158      )
    159    end
    160    vim.list_extend(non_static, {
    161      '#define DEFINE_FUNC_ATTRIBUTES',
    162      '#include "nvim/func_attr.h"',
    163      '#undef DEFINE_FUNC_ATTRIBUTES',
    164      '#ifndef DLLEXPORT',
    165      '#  ifdef MSWIN',
    166      '#    define DLLEXPORT __declspec(dllexport)',
    167      '#  else',
    168      '#    define DLLEXPORT',
    169      '#  endif',
    170      '#endif',
    171    })
    172    vim.list_extend(non_static, non_static_decls)
    173    non_static[#non_static + 1] = '#include "nvim/func_attr.h"'
    174    write_file(non_static_fname, non_static)
    175  end
    176 end
    177 
    178 return main()