use strict;
use warnings;
use Getopt::Long;
use PerfectHash;
my $output_path = '';
my $extern = 0;
my $case_fold = 1;
my $varname = 'ScanKeywords';
my $dialect = '';
GetOptions(
'output:s' => \$output_path,
'extern' => \$extern,
'case-fold!' => \$case_fold,
'varname:s' => \$varname,
'dialect:s' => \$dialect) || usage();
my $kw_input_file = shift @ARGV || die "No input file.\n";
if ($output_path ne '' && substr($output_path, -1) ne '/')
{
$output_path .= '/';
}
$kw_input_file =~ /(\w+)\.h$/ || die "Input file must be named something.h.\n";
if ($dialect ne '')
{
$dialect = '_' . $dialect;
}
my $base_filename = $1 . $dialect . '_d';
my $kw_def_file = $output_path . $base_filename . '.h';
open(my $kif, '<', $kw_input_file) || die "$kw_input_file: $!\n";
open(my $kwdef, '>', $kw_def_file) || die "$kw_def_file: $!\n";
printf $kwdef <<EOM, $base_filename, uc $base_filename, uc $base_filename;
/*-------------------------------------------------------------------------
*
* %s.h
* List of keywords represented as a ScanKeywordList.
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* NOTES
* ******************************
* *** DO NOT EDIT THIS FILE! ***
* ******************************
*
* It has been GENERATED by src/tools/gen_keywordlist.pl
*
*-------------------------------------------------------------------------
*/
#ifndef %s_H
#define %s_H
#include "kwlookup.h"
EOM
# Parse input file for keyword names.
my @keywords;
while (<$kif>)
{
if (/^OG_KEYWORD\("(\w+)"/)
{
push @keywords, $1;
}
}
if ($case_fold)
{
foreach my $kw (@keywords)
{
die qq|The keyword "$kw" is not lower-case in $kw_input_file\n|
if ($kw ne lc $kw);
}
}
for my $i (0..$#keywords - 1)
{
die qq|The keyword "$keywords[$i + 1]" is out of order in $kw_input_file\n|
if ($keywords[$i] cmp $keywords[$i + 1]) >= 0;
}
printf $kwdef qq|static const char %s_kw_string[] =\n\t"|, $varname;
print $kwdef join qq|\\0"\n\t"|, @keywords;
print $kwdef qq|";\n\n|;
printf $kwdef "static const uint16 %s_kw_offsets[] = {\n", $varname;
my $offset = 0;
my $max_len = 0;
foreach my $name (@keywords)
{
my $this_length = length($name);
print $kwdef "\t$offset,\n";
$offset += $this_length + 1;
$max_len = $this_length if $max_len < $this_length;
}
print $kwdef "};\n\n";
printf $kwdef "#define %s_NUM_KEYWORDS %d\n\n", uc $varname, scalar @keywords;
my $funcname = $varname . "_hash_func";
my $f = PerfectHash::generate_hash_function(\@keywords, $funcname,
case_fold => $case_fold);
printf $kwdef qq|static %s\n|, $f;
printf $kwdef "static " if !$extern;
printf $kwdef "const ScanKeywordList %s = {\n", $varname;
printf $kwdef qq|\t%s_kw_string,\n|, $varname;
printf $kwdef qq|\t%s_kw_offsets,\n|, $varname;
printf $kwdef qq|\t%s,\n|, $funcname;
printf $kwdef qq|\t%s_NUM_KEYWORDS,\n|, uc $varname;
printf $kwdef qq|\t%d\n|, $max_len;
printf $kwdef "};\n\n";
printf $kwdef "#endif\t\t\t\t\t\t\t/* %s_H */\n", uc $base_filename;
sub usage
{
die <<EOM;
Usage: gen_keywordlist.pl [--output/-o <path>] [--varname/-v <varname>] [--extern/-e] [--[no-]case-fold] input_file
--output Output directory (default '.')
--varname Name for ScanKeywordList variable (default 'ScanKeywords')
--extern Allow the ScanKeywordList variable to be globally visible
--no-case-fold Keyword matching is to be case-sensitive
gen_keywordlist.pl transforms a list of keywords into a ScanKeywordList.
The output filename is derived from the input file by inserting _d,
for example kwlist_d.h is produced from kwlist.h.
EOM
}