diff options
| author | Thiago de Arruda <tpadilha84@gmail.com> | 2014-06-02 11:24:02 -0300 | 
|---|---|---|
| committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-06-02 11:24:02 -0300 | 
| commit | cab8cf970c09ea465d30e11eb356e2e5d37dc544 (patch) | |
| tree | 5d274c892e4d53f5e976ae8f6f58aba030785e02 /scripts | |
| parent | 52a9a5b0b0c53a1481d901f39ed0d1e7e86c3853 (diff) | |
| parent | 4aecb71b0e819aa84a430dacdab2146229c410a5 (diff) | |
| download | rneovim-cab8cf970c09ea465d30e11eb356e2e5d37dc544.tar.gz rneovim-cab8cf970c09ea465d30e11eb356e2e5d37dc544.tar.bz2 rneovim-cab8cf970c09ea465d30e11eb356e2e5d37dc544.zip | |
Merge pull request #710 'Automatically generate declarations'
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/finddeclarations.pl | 50 | ||||
| -rwxr-xr-x | scripts/gendeclarations.lua | 232 | ||||
| -rwxr-xr-x | scripts/movedocs.pl | 180 | ||||
| -rw-r--r-- | scripts/msgpack-gen.lua | 4 | ||||
| -rwxr-xr-x | scripts/stripdecls.py | 141 | 
5 files changed, 606 insertions, 1 deletions
| diff --git a/scripts/finddeclarations.pl b/scripts/finddeclarations.pl new file mode 100755 index 0000000000..1b1a57b9b7 --- /dev/null +++ b/scripts/finddeclarations.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +if ($ARGV[0] eq '--help') { +  print << "EOF"; +Usage: + +  $0 definitions.c +EOF +  exit; +} + +my ($cfname, $sfname, $gfname, $cpp) = @ARGV; + +my $F; + +open $F, "<", $cfname; + +my $text = join "", <$F>; + +close $F; + +my $s = qr/(?>\s*)/aso; +my $w = qr/(?>\w+)/aso; +my $argname = qr/$w(?:\[(?>\w+)\])?/aso; +my $type_regex = qr/(?:$w$s\**$s)+/aso; +my $arg_regex = qr/(?:$type_regex$s$argname)/aso; + +while ($text =~ / +    (?<=\n)         # Definition starts at the start of line +    $type_regex     # Return type +    $s$w            # Function name +    $s\($s +    (?: +       $arg_regex(?:$s,$s$arg_regex)*+ +       ($s,$s\.\.\.)?                   # varargs function +      |void +    )? +    $s\) +    (?:$s FUNC_ATTR_$w(?:\((?>[^)]*)\))?)*+ # Optional attributes +    (?=$s;)         # Ending semicolon +  /axsogp) { +  my $match = "${^MATCH}"; +  my $s = "${^PREMATCH}"; +  $s =~ s/[^\n]++//g; +  my $line = 1 + length $s; +  print "${cfname}:${line}: $match\n"; +} diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua new file mode 100755 index 0000000000..311ed8b527 --- /dev/null +++ b/scripts/gendeclarations.lua @@ -0,0 +1,232 @@ +#!/usr/bin/lua + +local fname = arg[1] +local static_fname = arg[2] +local non_static_fname = arg[3] +local cpp = arg[4] + +cpp = cpp:gsub(' %-DINCLUDE_GENERATED_DECLARATIONS ', ' ') + +local lpeg = require('lpeg') + +local fold = function (func, ...) +  local result = nil +  for i, v in ipairs({...}) do +    if result == nil then +      result = v +    else +      result = func(result, v) +    end +  end +  return result +end + +local folder = function (func) +  return function (...) +    return fold(func, ...) +  end +end + +local lit = lpeg.P +local set = function(...) +  return lpeg.S(fold(function (a, b) return a .. b end, ...)) +end +local any_character = lpeg.P(1) +local rng = function(s, e) return lpeg.R(s .. e) end +local concat = folder(function (a, b) return a * b end) +local branch = folder(function (a, b) return a + b end) +local one_or_more = function(v) return v ^ 1 end +local two_or_more = function(v) return v ^ 2 end +local any_amount = function(v) return v ^ 0 end +local one_or_no = function(v) return v ^ -1 end +local look_behind = lpeg.B +local look_ahead = function(v) return #v end +local neg_look_ahead = function(v) return -v end +local neg_look_behind = function(v) return -look_behind(v) end + +local w = branch( +  rng('a', 'z'), +  rng('A', 'Z'), +  lit('_') +) +local aw = branch( +  w, +  rng('0', '9') +) +local s = set(' ', '\n', '\t') +local raw_word = concat(w, any_amount(aw)) +local right_word = concat( +  raw_word, +  neg_look_ahead(aw) +) +local word = concat( +  neg_look_behind(aw), +  right_word +) +local spaces = any_amount(branch( +  s, +  -- Comments are really handled by preprocessor, so the following is not needed +  concat( +    lit('/*'), +    any_amount(concat( +      neg_look_ahead(lit('*/')), +      any_character +    )), +    lit('*/') +  ), +  concat( +    lit('//'), +    any_amount(concat( +      neg_look_ahead(lit('\n')), +      any_character +    )), +    lit('\n') +  ) +)) +local typ_part = concat( +  word, +  any_amount(concat( +    spaces, +    lit('*') +  )), +  spaces +) +local typ = one_or_more(typ_part) +local typ_id = two_or_more(typ_part) +local arg = typ_id         -- argument name is swallowed by typ +local pattern = concat( +  typ_id,                  -- return type with function name +  spaces, +  lit('('), +  spaces, +  one_or_no(branch(        -- function arguments +    concat( +      arg,                 -- first argument, does not require comma +      any_amount(concat(   -- following arguments, start with a comma +        spaces, +        lit(','), +        spaces, +        arg, +        any_amount(concat( +          lit('['), +          spaces, +          any_amount(aw), +          spaces, +          lit(']') +        )) +      )), +      one_or_no(concat( +        spaces, +        lit(','), +        spaces, +        lit('...') +      )) +    ), +    lit('void')            -- also accepts just void +  )), +  spaces, +  lit(')'), +  any_amount(concat(       -- optional attributes +    spaces, +    lit('FUNC_ATTR_'), +    any_amount(aw), +    one_or_no(concat(      -- attribute argument +      spaces, +      lit('('), +      any_amount(concat( +        neg_look_ahead(lit(')')), +        any_character +      )), +      lit(')') +    )) +  )), +  look_ahead(concat(       -- definition must be followed by "{" +    spaces, +    lit('{') +  )) +) + +if fname == '--help' then +  print'Usage:' +  print() +  print'  gendeclarations.lua definitions.c static.h non-static.h "cc -E …"' +  os.exit() +end + +local pipe = io.popen(cpp .. ' -DDO_NOT_DEFINE_EMPTY_ATTRIBUTES ' .. fname, 'r') +local text = pipe:read('*a') +if not pipe:close() then +  os.exit(2) +end + +local header = [[ +#ifndef DEFINE_FUNC_ATTRIBUTES +# define DEFINE_FUNC_ATTRIBUTES +#endif +#include "nvim/func_attr.h" +#undef DEFINE_FUNC_ATTRIBUTES +]] + +local footer = [[ +#include "nvim/func_attr.h" +]] + +local non_static = header +local static = header + +local filepattern = '^# %d+ "[^"]-/?([^"/]+)"' +local curfile + +init = 0 +curfile = nil +neededfile = fname:match('[^/]+$') +while init ~= nil do +  init = text:find('\n', init) +  if init == nil then +    break +  end +  init = init + 1 +  if text:sub(init, init) == '#' then +    file = text:match(filepattern, init) +    if file ~= nil then +      curfile = file +    end +  elseif curfile == neededfile then +    s = init +    e = pattern:match(text, init) +    if e ~= nil then +      local declaration = text:sub(s, e - 1) +      -- Comments are really handled by preprocessor, so the following is not  +      -- needed +      declaration = declaration:gsub('/%*.-%*/', '') +      declaration = declaration:gsub('//.-\n', '\n') + +      declaration = declaration:gsub('\n', ' ') +      declaration = declaration:gsub('%s+', ' ') +      declaration = declaration:gsub(' ?%( ?', '(') +      declaration = declaration:gsub(' ?%) ?', ')') +      declaration = declaration:gsub(' ?, ?', ', ') +      declaration = declaration:gsub(' ?(%*+) ?', ' %1') +      declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') +      declaration = declaration:gsub(' $', '') +      declaration = declaration .. ';\n' +      if text:sub(s, s + 5) == 'static' then +        static = static .. declaration +      else +        non_static = non_static .. declaration +      end +    end +  end +end + +non_static = non_static .. footer +static = static .. footer + +local F +F = io.open(static_fname, 'w') +F:write(static) +F:close() + +F = io.open(non_static_fname, 'w') +F:write(non_static) +F:close() diff --git a/scripts/movedocs.pl b/scripts/movedocs.pl new file mode 100755 index 0000000000..923c633f13 --- /dev/null +++ b/scripts/movedocs.pl @@ -0,0 +1,180 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +if ($ARGV[0] eq '--help') { +  print << "EOF"; +Usage: + +  $0 file.h file.c + +Removes documentation attached to function declarations in file.h and adds them  +to function definitions found in file.c. + +  $0 file.c + +Moves documentation attached to function declaration present in the same file as  +the definition. +EOF +  exit 0; +} + +my $hfile = shift @ARGV; +my @cfiles = @ARGV; + +my %docs = (); +my $F; + +sub write_lines { +  my $file = shift; +  my @lines = @_; + +  my $F; + +  open $F, '>', $file; +  print $F (join "", @lines); +  close $F; +} + +if (@cfiles) { +  open $F, '<', $hfile +    or die "Failed to open $hfile."; + +  my @hlines = (); + +  my $lastdoc = ''; + +  while (<$F>) { +    if (/^\/\/\/?/) { +      $lastdoc .= $_; +    } elsif (/^\S.*?(\w+)\(.*(?:,|\);?|FUNC_ATTR_\w+;?)$/) { +      die "Documentation for $1 was already defined" if (defined $docs{$1}); +      if ($lastdoc ne '') { +        $docs{$1} = $lastdoc; +        $lastdoc = ''; +      } +      push @hlines, $_; +    } elsif ($lastdoc ne '') { +      push @hlines, $lastdoc; +      $lastdoc = ''; +      push @hlines, $_; +    } else { +      push @hlines, $_; +    } +  } + +  close $F; + +  my %clines_hash = (); + +  for my $cfile (@cfiles) { +    open $F, '<', $cfile +      or die "Failed to open $cfile."; + +    my @clines = (); + +    while (<$F>) { +      if (/^\S.*?(\w+)\(.*[,)]$/ and defined $docs{$1}) { +        push @clines, $docs{$1}; +        delete $docs{$1}; +      } elsif (/^(?!static\s)\S.*?(\w+)\(.*[,)]$/ and not defined $docs{$1}) { +        print STDERR "Documentation not defined for $1\n"; +      } +      push @clines, $_; +    } + +    close $F; + +    $clines_hash{$cfile} = \@clines; +  } + +  while (my ($func, $value) = each %docs) { +    die "Function not found: $func\n"; +  } + +  write_lines($hfile, @hlines); +  while (my ($cfile, $clines) = each %clines_hash) { +    write_lines($cfile, @$clines); +  } +} else { +  open $F, '<', $hfile; + +  my @lines; + +  my $lastdoc = ''; +  my $defstart = ''; +  my $funcname; + +  sub clear_lastdoc { +    if ($lastdoc ne '') { +      push @lines, $lastdoc; +      $lastdoc = ''; +    } +  } + +  sub record_lastdoc { +    my $funcname = shift; +    if ($lastdoc ne '') { +      $docs{$funcname} = $lastdoc; +      $lastdoc = ''; +    } +  } + +  sub add_doc { +    my $funcname = shift; +    if (defined $docs{$funcname}) { +      push @lines, $docs{$funcname}; +      delete $docs{$funcname}; +    } +  } + +  sub clear_defstart { +    push @lines, $defstart; +    $defstart = ''; +  } + +  while (<$F>) { +    if (/\/\*/ .. /\*\// and not /\/\*.*?\*\//) { +      push @lines, $_; +    } elsif (/^\/\/\/?/) { +      $lastdoc .= $_; +    } elsif (/^\S.*?(\w+)\(.*(?:,|(\);?))$/) { +      if (not $2) { +        $defstart .= $_; +        $funcname = $1; +      } elsif ($2 eq ');') { +        record_lastdoc $1; +        push @lines, $_; +      } elsif ($2 eq ')') { +        clear_lastdoc; +        add_doc $1; +        push @lines, $_; +      } +    } elsif ($defstart ne '') { +      $defstart .= $_; +      if (/[{}]/) { +        clear_lastdoc; +        clear_defstart; +      } elsif (/\);$/) { +        record_lastdoc $funcname; +        clear_defstart; +      } elsif (/\)$/) { +        clear_lastdoc; +        add_doc $funcname; +        clear_defstart; +      } +    } else { +      clear_lastdoc; +      push @lines, $_; +    } +  } + +  close $F; + +  while (my ($func, $value) = each %docs) { +    die "Function not found: $func\n"; +  } + +  write_lines($hfile, @lines); +} diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 8d7bb8ea59..c8a09c8e96 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -94,7 +94,9 @@ output:write([[  ]])  for i = 1, #headers do -  output:write('\n#include "nvim/'..headers[i]..'"') +  if headers[i]:sub(-12) ~= '.generated.h' then +    output:write('\n#include "nvim/'..headers[i]..'"') +  end  end  output:write([[ diff --git a/scripts/stripdecls.py b/scripts/stripdecls.py new file mode 100755 index 0000000000..34be2a1578 --- /dev/null +++ b/scripts/stripdecls.py @@ -0,0 +1,141 @@ +#!/usr/bin/python +# vim: set fileencoding=utf-8: + +from __future__ import print_function, unicode_literals, division + +from clang.cindex import Index, CursorKind +from collections import namedtuple, OrderedDict, defaultdict +import sys +import os + + +DECL_KINDS = { +  CursorKind.FUNCTION_DECL, +} + + +Strip = namedtuple('Strip', 'start_line start_column end_line end_column') + + +def main(progname, cfname, only_static, move_all): +  only_static = False + +  cfname = os.path.abspath(os.path.normpath(cfname)) + +  hfname1 = os.path.splitext(cfname)[0] + os.extsep + 'h' +  hfname2 = os.path.splitext(cfname)[0] + '_defs' + os.extsep + 'h' + +  files_to_modify = (cfname, hfname1, hfname2) + +  index = Index.create() +  src_dirname = os.path.join(os.path.dirname(__file__), '..', 'src') +  src_dirname = os.path.abspath(os.path.normpath(src_dirname)) +  relname = os.path.join(src_dirname, 'nvim') +  unit = index.parse(cfname, args=('-I' + src_dirname, +                                   '-DUNIX', +                                   '-DEXITFREE', +                                   '-DFEAT_USR_CMDS', +                                   '-DFEAT_CMDL_COMPL', +                                   '-DFEAT_COMPL_FUNC', +                                   '-DPROTO', +                                   '-DUSE_MCH_ERRMSG')) +  cursor = unit.cursor + +  tostrip = defaultdict(OrderedDict) +  definitions = set() + +  for child in cursor.get_children(): +    if not (child.location and child.location.file): +      continue +    fname = os.path.abspath(os.path.normpath(child.location.file.name)) +    if fname not in files_to_modify: +      continue +    if child.kind not in DECL_KINDS: +      continue +    if only_static and next(child.get_tokens()).spelling == 'static': +      continue + +    if child.is_definition() and fname == cfname: +      definitions.add(child.spelling) +    else: +      stripdict = tostrip[fname] +      assert(child.spelling not in stripdict) +      stripdict[child.spelling] = Strip( +        child.extent.start.line, +        child.extent.start.column, +        child.extent.end.line, +        child.extent.end.column, +      ) + +  for (fname, stripdict) in tostrip.items(): +    if not move_all: +      for name in set(stripdict) - definitions: +        stripdict.pop(name) + +    if not stripdict: +      continue + +    if fname.endswith('.h'): +      is_h_file = True +      include_line = next(reversed(stripdict.values())).start_line + 1 +    else: +      is_h_file = False +      include_line = next(iter(stripdict.values())).start_line + +    lines = None +    generated_existed = os.path.exists(fname + '.generated.h') +    with open(fname, 'rb') as F: +      lines = list(F) + +    stripped = [] + +    for name, position in reversed(stripdict.items()): +      sl = slice(position.start_line - 1, position.end_line) +      if is_h_file: +        include_line -= sl.stop - sl.start +      stripped += lines[sl] +      lines[sl] = () + +    if not generated_existed: +      lines[include_line:include_line] = [ +        '#ifdef INCLUDE_GENERATED_DECLARATIONS\n', +        '# include "{0}.generated.h"\n'.format(os.path.relpath(fname, relname)), +        '#endif\n', +      ] + +    with open(fname, 'wb') as F: +      F.writelines(lines) + + +if __name__ == '__main__': +  progname = sys.argv[0] +  args = sys.argv[1:] +  if not args or '--help' in args: +    print('Usage:') +    print('') +    print('  {0} [--static [--all]] file.c...'.format(progname)) +    print('') +    print('Stripts all declarations from file.c, file.h and file_defs.h.') +    print('If --static argument is given then only static declarations are') +    print('stripped. Declarations are stripped only if corresponding') +    print('definition is found unless --all argument was given.') +    print('') +    print('Note: it is assumed that static declarations starts with "static"') +    print('      keyword.') +    sys.exit(0 if args else 1) + +  if args[0] == '--static': +    only_static = True +    args = args[1:] +  else: +    only_static = False + +  if args[0] == '--all': +    move_all = True +    args = args[1:] +  else: +    move_all = False + +  for cfname in args: +    print('Processing {0}'.format(cfname)) +    main(progname, cfname, only_static, move_all) | 
